Elenco di inizializzatori all'interno di std :: pair


26

Questo codice:

#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;
}

compila ma restituisce segfault. Perché?

Testato su gcc 8.3.0 e sui compilatori online.


1
Per comodità: Godbolt si collega con e senza std::pair .
Max Langhof,

Risposte:


24

std::initializer_listnon è pensato per essere memorizzato, è solo per ... inizializzazione. Internamente memorizza solo un puntatore al primo elemento e alla dimensione. Nel tuo codice gli std::stringoggetti sono temporanei e initializer_listnessuno dei due ne prende la proprietà, né prolunga la loro vita, né li copia (perché non è un contenitore) in modo da uscire dall'ambito immediatamente dopo la creazione, ma hai initializer_listancora un puntatore su di essi. Ecco perché si ottiene un errore di segmentazione.

Per la conservazione è necessario utilizzare un contenitore, come std::vectoro std::array.


Mi dà fastidio che sia compilabile. Linguaggio sciocco :(
Razze di leggerezza in orbita il

1
@LightnessRaceswithMonica Ho un sacco di carne con initializer_list . Non è possibile utilizzare oggetti di solo spostamento, quindi ad esempio non è possibile utilizzare list init con il vettore di unique_ptr. La dimensione di initializer_listnon è una costante di compilazione. E il fatto che std::vector<int>(3)e std::vector<int>{3}fare cose completamente diverse. Mi rende triste :(
bolov

Sì lo stesso ... :(
Razze di leggerezza in orbita il

3

Vorrei solo aggiungere un po 'più di dettagli. Un array sottostante std::initializer_listsi comporta in modo simile ai provvisori. Considera la seguente classe:

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

e il suo utilizzo nel seguente codice:

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

Si stampa

ctor
dtor
barrier

poiché nella prima riga, Xviene creata un'istanza temporanea di tipo (convertendo il costruttore da 1) e distrutta. Il riferimento memorizzato pè quindi penzolante.

Per quanto riguarda std::initializer_list, se lo usi in questo modo:

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

quindi, l'array sottostante (temporaneo) esiste fino a quando lesce. Pertanto, l'output è:

ctor
ctor
barrier
dtor
dtor

Tuttavia, se si passa a

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

L'output è di nuovo

ctor
dtor
barrier

poiché l'array sottostante (temporaneo) esiste solo nella prima riga. Dereferenziare il puntatore agli elementi di lallora comporta un comportamento indefinito.

La demo live è qui .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.