Liste d'initialisation dans std :: pair

26

Ce code:

#include <iostream>
#include <string>

std::pair<std::initializer_list<std::string>, int> groups{ { "A", "B" }, 0 };

int main()
{
    for (const auto& i : groups.first)
    {
        std::cout << i << '\n';
    }
    return 0;
}

compile mais renvoie segfault. Pourquoi?

Testé sur gcc 8.3.0 et sur des compilateurs en ligne.

rin
la source
1
Pour plus de commodité: Godbolt relie avec et sans std::pair .
Max Langhof

Réponses:

24

std::initializer_listn'est pas destiné à être stocké, il est juste destiné à ... bien l'initialisation. En interne, il stocke juste un pointeur sur le premier élément et la taille. Dans votre code, les std::stringobjets sont temporaires et initializer_listni l'un ni l' autre ne les prend en charge, ne prolonge leur durée de vie, ni ne les copie (car ce n'est pas un conteneur) afin qu'ils sortent du champ d'application immédiatement après la création, mais votre initializer_listdétient toujours un pointeur sur eux. C'est pourquoi vous obtenez un défaut de segmentation.

Pour le stockage, vous devez utiliser un conteneur, comme std::vectorou std::array.

bolov
la source
Cela me dérange que ce soit compilable. Langage idiot :(
Courses de légèreté en orbite
1
@LightnessRaceswithMonica J'ai beaucoup de boeuf avec initializer_list. Il n'est pas possible d'utiliser des objets à déplacement uniquement, vous ne pouvez donc pas utiliser list init avec le vecteur de unique_ptr par exemple. La taille de initializer_listn'est pas une constante au moment de la compilation. Et le fait que std::vector<int>(3)et std::vector<int>{3}faire des choses complètement différentes. Me rend triste :(
bolov
Ouais même ... :(
Courses de légèreté en orbite
3

Je voudrais juste ajouter un peu plus de détails. Un tableau sous-jacent de std::initializer_listse comporte de manière similaire aux temporaires. Considérez la classe suivante:

struct X
{
   X(int i) { std::cerr << "ctor\n"; }
   ~X() { std::cerr << "dtor\n"; }
};

et son utilisation dans le code suivant:

std::pair<const X&, int> p(1, 2);
std::cerr << "barrier\n";

Il imprime

ctor
dtor
barrier

car à la première ligne, une instance temporaire de type Xest créée (en convertissant le constructeur à partir de 1) et détruite également. La référence stockée dans pest alors suspendue.

Quant à std::initializer_list, si vous l'utilisez de cette façon:

{
   std::initializer_list<X> l { 1, 2 };
   std::cerr << "barrier\n";
}

puis, le tableau sous-jacent (temporaire) existe tant qu'il existe l. Par conséquent, la sortie est:

ctor
ctor
barrier
dtor
dtor

Cependant, si vous passez à

std::pair<std::initializer_list<X>, int> l { {1}, 2 };
std::cerr << "barrier\n";

La sortie est à nouveau

ctor
dtor
barrier

puisque le tableau sous-jacent (temporaire) n'existe qu'à la première ligne. Déréférencer le pointeur sur les éléments de lentraîne alors un comportement indéfini.

La démo en direct est ici .

Daniel Langr
la source