it-swarm.com.de

std :: initializer_list, geschweifte Initialisierung und Header

Beim Lesen eines anderen Themas bin ich auf ein seltsames Verhalten gestoßen, zumindest für mich ... Dieser ganze Gedanke stammt von den speziellen Interaktionen zwischen auto und geschweiften Klammern. Wenn du so etwas schreibst:

auto A = { 1, 2, 3 }

der Compiler leitet A als std::initializer_list ab. Das Seltsame ist, dass eine ähnliche Regel nicht nur für auto gilt, wo es besondere Gründe dafür geben kann, sondern auch für andere Dinge. Wenn Sie Folgendes schreiben:

template<typename T>
void f(std::vector<T> Vector)
{
    // do something
}

man kann es natürlich nicht so nennen:

f({ 1, 2, 3});

obwohl ein std::vector initialisiert werden kann. Wenn Sie jedoch std::vector durch std::initializer_list ersetzen, funktioniert der Aufruf und der Compiler leitet int ordnungsgemäß als Typ T ab. Interessanter ist jedoch, dass Sie im ersten Fall #include <vector>, im zweiten Fall #include <initializer_list> nicht brauchen. Das brachte mich zum Nachdenken und nach einem Test wurde mir klar, dass std::initializer_list keinen eigenen Header benötigt, daher ist er in gewisser Weise Teil der "Basis" -Features.

Damit alles einen Sinn ergibt, sollte std::initializer_list für Standardobjekte mehr oder weniger die gleiche Art sein wie Lambdas für aufrufbare Objekte (im engsten Sinne ein Objekt mit einer operator()). Mit anderen Worten, unbenannte geschweifte Definitionen sollten standardmäßig std::initializer_list verwendet werden, genau wie Lambdas (meistens) unbenannte aufrufbare Objekte sind.

Ist das richtig? Kann dieses Verhalten außerdem geändert werden und wenn ja, wie?

UPDATE: Es wurde festgestellt, dass der Header für initializer_list von iostream transitiv eingeschlossen wurde (wirklich komisch). Es bleibt jedoch die Frage: Warum funktioniert der Aufruf für std::initializer_list und nicht für std::vector?

14
Andrea Bocco

Es ist schlecht geformt ( es erfordert eine Diagnose ), um den initializer_list-Header nicht einzuschließen, wenn wir std::initializer_list verwenden. Wir können dies aus [dcl.init.list] p2 sehen:

... Die Vorlage std :: initializer_list ist nicht vordefiniert. wenn der Header <initializer_list> .__ ist. nicht vor einer Verwendung von std :: initializer_list enthalten - sogar eine implizite Verwendung, bei der der Typ nicht .__ ist. benannt (9.1.7.4) - das Programm ist schlecht geformt.

Meistens ist es wahrscheinlich, dass Sie den Header transitiv einfügen, der zwar gut geformt ist, Ihren Code jedoch anfälliger macht. Geben Sie also an, was Sie verwenden.

Wir können aus einem Live-Godbolt-Beispiel ersehen, dass wir, wenn wir keine Include-Elemente enthalten, die erforderliche Diagnose von gcc/clang/MSVC z.B. erhalten.

error: use of undeclared identifier 'std'    
void foo( std::initializer_list<int>) {
          ^

und einschließlich <vector> oder <iostream> erhalten wir keine Diagnose mehr .

Warum es nicht wie erwartet ableitet, wird durch [temp.deduct.type] p5 abgedeckt. Dies besagt, dass dies ein nicht abgeleiteter Kontext ist:

Die nicht abgeleiteten Kontexte sind:
...
- Ein Funktionsparameter, für den das zugehörige Argument eine Initialisierungsliste ist ([dcl.init.list]), der Parameter hat jedoch keinen Typ, für den die Ableitung von einer Initialisierungsliste angegeben wird ([temp.deduct.call]).> [Beispiel:

template<class T> void g(T);
g({1,2,3});                 // error: no argument deduced for T

- Ende Beispiel]
...

siehe auch [temp.deduct.call] p1 :

... Andernfalls bewirkt ein Initialisierungslistenargument, dass der Parameter als nicht abgeleiteter Kontext betrachtet wird ([temp.deduct.type]) ...

9
Shafik Yaghmour

Wahrscheinlich fügen Sie den Header von <vector> oder <iostream> aus transitiv hinzu. Beachten Sie, dass der Standard für den std::vector-Fall explizit einen nicht abgeleiteten Kontext erzwingt

[temp.deduct.type]/p5

Die nicht abgeleiteten Kontexte sind:

...

  • Ein Funktionsparameter, für den das zugehörige Argument eine Initialisierungsliste ist ([dcl.init.list]), der Parameter hat jedoch keinen Typ, für den die Ableitung von einer Initialisierungsliste angegeben wird ([temp.deduct.call]).

Cfr. cpp reference ex.6

1
Marco A.

Die CPP-Online-Referenz für <vector> zeigt, dass <initializer_list> in der Kopfzeile enthalten ist.

Die Implementierung von <vector> durch GCC umfasst <initializer_list>. Dies gilt wahrscheinlich auch für andere Implementierungen. Aus diesem Grund mussten Sie <initializer_list> nicht separat angeben.

Schauen Sie sich diese Quelle für GCC 4.6.2 an, die <initializer_list> im <vector>-Header enthält . https://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a01069_source.html

0
P.W