web-dev-qa-db-fra.com

std :: initializer_list, initialisation soulignée et en-tête

En lisant sur un sujet différent, je suis tombé sur un comportement étrange, du moins pour moi… .. Toute cette pensée est née des interactions spéciales entre auto et les accolades. Si vous écrivez quelque chose comme:

auto A = { 1, 2, 3 }

le compilateur déduira que A est un std::initializer_list. La chose étrange est qu’une règle similaire s’applique non seulement à auto, où il peut y avoir des raisons particulières, mais aussi à d’autres choses . Si vous écrivez ceci:

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

vous ne pouvez bien sûr pas l'appeler de cette façon:

f({ 1, 2, 3});

même si un std::vector peut être initialisé avec un support. Toutefois, si vous remplacez std::vector par std::initializer_list, l'appel fonctionne et le compilateur déduira correctement int en tant que type T. La chose la plus intéressante est toutefois que dans le premier cas, vous devez #include <vector>, dans le dernier cas, vous n'avez pas besoin de #include <initializer_list>. Cela m'a fait réfléchir et après un test, j'ai réalisé que d'une manière ou d'une autre, std::initializer_list n'avait pas besoin de son propre en-tête, il fait donc partie des fonctionnalités de "base".

De plus, pour que tout ait du sens, std::initializer_list devrait s’appliquer plus ou moins aux objets standard de la même façon que les lambdas sont des objets appelables (au sens le plus strict, c’est un objet avec une operator()). En d'autres termes, les définitions entre accolades non nommées doivent par défaut être std::initializer_list, tout comme les lambdas sont (généralement) des objets appelables non nommés.

Ce raisonnement est-il correct? De plus, ce comportement peut-il être changé et, si oui, comment?

UPDATE: l'en-tête de initializer_list a été inclus de manière transitoire à partir de iostream (vraiment étrange). Cependant, la question demeure: pourquoi l'appel fonctionne pour std::initializer_list et non pour std::vector?

14
Andrea Bocco

Il est mal formé ( il nécessite donc un diagnostic ) pour ne pas inclure l'en-tête initializer_list si nous utilisons std::initializer_list. Nous pouvons le voir dans [dcl.init.list] p2 :

... Le modèle std :: initializer_list n'est pas prédéfini; si l'en-tête <initializer_list> est non inclus avant une utilisation de std :: initializer_list - même une utilisation implicite dans laquelle le type n'est pas named (9.1.7.4) - le programme est mal formé.

Il est fort probable que vous incluiez l'en-tête de manière transitoire, ce qui est bien formé mais rend votre code plus fragile, alors incluez ce que vous utilisez.

Nous pouvons voir à partir d’un exemple en direct que nous n’avons pas d’inclusion, nous obtenons le diagnostic requis de gcc/clang/MSVC p.

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

et en incluant <vector> ou <iostream> nous n'obtenons plus de diagnostic .

La raison pour laquelle elle ne déduit pas comme prévu est couverte par [temp.deduct.type] p5 qui nous indique qu'il s'agit d'un contexte non déduit:

Les contextes non déduits sont:
...
- Paramètre de fonction pour lequel l'argument associé est une liste d'initialiseurs ([dcl.init.list]) mais dont le paramètre n'a pas de type pour lequel la déduction d'une liste d'initialiseurs est spécifiée ([temp.deduct.call]).> [ Exemple:

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

- fin exemple]
...

voir aussi [temp.deduct.call] p1 :

... Sinon, un argument de la liste d'initialisation fait en sorte que le paramètre soit considéré comme un contexte non déduit ([temp.deduct.type]) ...

9
Shafik Yaghmour

Vous incluez probablement l'en-tête de manière transitoire à partir de <vector> ou <iostream>, gardez à l'esprit que la norme applique explicitement un contexte non déduit pour le cas std::vector

[temp.deduct.type]/p5

Les contextes non déduits sont:

...

  • Paramètre de fonction pour lequel l'argument associé est une liste d'initialiseurs ([dcl.init.list]) mais dont le paramètre n'a pas de type pour lequel une déduction est spécifiée ([temp.deduct.call]).

Cfr. cppreference ex.6

1
Marco A.

La référence en ligne CPP pour <vector> indique que <initializer_list> est inclus dans son en-tête.

L'implémentation de <vector> par GCC inclut <initializer_list>. C'est probablement le cas d'autres implémentations. C'est la raison pour laquelle vous n'avez pas à inclure <initializer_list> séparément.

Découvrez cette source pour GCC 4.6.2 qui inclut <initializer_list> dans l'en-tête <vector> . https://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a01069_source.html

0
P.W