2015-01-28 13 views
13

Proszę rozważyć następujący kod:różne zachowania C++ 11 list-inicjalizacji

class A { 
private: 
    std::string s; 
public: 
    A() = delete; 
    A(const A&) = delete; 
    A(A&&) = delete; 
    A(const std::string &a) : s(a) {} 
}; 

Teraz chciałbym zainicjować tablicę A Korzystanie z listy inicjalizacji. g ++ (4.9.1) może skutecznie budować następujący kod:

int main() { 
    A arr[2] = {{"a"}, {"b"}}; 
    return 0; 
} 

Ale to nie powiodło się dla następującego kodu:

class Aggr { 
private: 
    A arr[2]; 
public: 
    Aggr() : arr{{"a"}, {"b"}} {} 
}; 

Komunikaty o błędach,

test.cc: In constructor ‘Aggr::Aggr()’: 
test.cc:22:28: error: use of deleted function ‘A::A(A&&)’ 
    Aggr() : arr{{"a"}, {"b"}} {} 
          ^   
test.cc:11:3: note: declared here 
    A(A&&) = delete; 
^

Powiedział , inicjator list próbuje wywołać konstruktor ruchu w celu zainicjowania tablicy wewnątrz klasy. Ten kod został jednak pomyślnie zbudowany przez clang v3.5 bez żadnych ostrzeżeń. Więc chciałbym wiedzieć, co C++ 11 (lub nowsza wersja) określa reguły w odniesieniu do inicjowania listy. Z góry dziękuję.

+0

Dlaczego to jest "A &&"? – ZivS

+0

@ZivS numer referencyjny rvalue – bolov

+3

może odnosić się do twojego problemu: http://stackoverflow.com/questions/26685551/how-to-initialize-array-of-classes-with-deleted-copy-constructor-c11 – marcinj

Odpowiedz

2

Czytanie na nowo standardu, myślę, że to błąd.

Co oznacza standard?

8.5.1/2 Kiedy agregat jest inicjowany przez liście inicjatora, zgodnie 8.5.4, elementy listy inicjatora są brane jak inicjalizatory członów agregatu, w podnoszeniu indeksu dolnego lub elementu członkowskiego. Każdy element jest inicjowany w trybie kopiowania z odpowiedniej klauzuli inicjalizującej- .

To wyjaśnia, że:

8.5/14: (...) nazywa kopiowaniem inicjalizacji. [Uwaga: inicjowanie kopii może wywołać ruch (12.8). -wstrzymaj notatkę]

Ale nie znalazłem dowodów w 12.8, że w twoim konkretnym przypadku ruch byłby wymagany.

8.5.4/3 W przeciwnym razie, jeśli T jest typem klasy, konstruktorzy są uznawane. Jeśli T ma konstruktor listy inicjalizacyjnej, lista argumentów zawiera listę inicjalizatorów jako pojedynczy argument; w przeciwnym razie, lista argumentów składa się z elementów listy inicjalizatora. Odpowiednie konstruktory są wyliczane, a najlepiej wybrane poprzez rozdzielczość przeciążenia (13.3).

Zasadniczo kod powinien zadziałać!

Czy to błąd? Próbowałem eksperymentalnie

Skomentowałem usunięcie konstruktora ruchu, aby skorzystać z niejawnego konstruktora ruchu.O dziwo, otrzymałem następujący komunikat o błędzie:

Compilation error time: 0 memory: 3232 signal:0 

prog.cpp: In constructor 'Aggr::Aggr()': 
prog.cpp:19:28: error: use of deleted function 'A::A(const A&)' 
    Aggr() : arr{{"a"}, {"b"}} {} 
          ^
prog.cpp:10:3: note: declared here 
    A(const A&) = delete 

Teraz skarży się na brakujący konstruktor kopii!

Jeszcze dziwniej, więc podałem własny konstruktor ruchu zamiast domyślnego: tutaj skompilował kod pomyślnie!

Wreszcie warunkiem zarówno kopię oraz ruch i dodano kilka śledzenie:

class A { 
private: 
    std::string s; 
public: 
    A() = delete; 
    A(const A&) { std::cout<<"copy\n";} //= delete; 
    A(A&&) { std::cout<<"move\n";} //= delete; 
    A(const std::string &a) : s(a) { std::cout<<"string ctor\n";} 
}; 

I kiedy utworzyć obiekt Aggr, to po prostu wyświetla:

string ctor 
string ctor 

pokazując, że element tablicy jest zainicjowano formularz konstruktora ciągu za pomocą metody "copy elision", tak jak się spodziewaliśmy.

Wszystkie te testy przeprowadzono z gcc-9.4.2 na idee z opcją C++ 14.

Wnioski

Fakt, że ten sam kod nie skompilować z niejawnego przenieść ctor i udaje ze zdefiniowaną przez użytkownika posunięcie konstruktor wygląda bardzo poważnie jak robaka.

Fakt, że konstruktor ruchu nie jest używany, gdy jest dostępny, wzmacnia to wrażenie.

W związku z tym zgłosiłem this bug.

+0

Dobra odpowiedź i dziękuję za zgłoszenie tego błędu. – Destructor