2017-12-26 110 views
16

Naprawdę dziwne i nieoczekiwane zachowanie klangu 5 zostało wykryte po przejściu na C++ 17 i zastąpieniu niestandardowego rozwiązania std::optional standardowym. Z jakiegoś powodu, emplace() został wyłączony z powodu błędnej oceny cechy std::is_constructible klasy parametrów.clang 5: std :: opcjonalne wkręty do instancji std :: is_constructible cecha parametru typ

pewnych specyficznych warunków musi być spełniony przed reprodukuje:

#include <optional> 

/// Precondition #1: T must be a nested struct 
struct Foo 
{ 
    struct Victim 
    { 
     /// Precondition #2: T must have an aggregate-initializer 
     /// for one of its members 
     std::size_t value{0}; 
    }; 

    /// Precondition #3: std::optional<T> must be instantiated in this scope 
    std::optional<Victim> victim; 

    bool foo() 
    { 
     std::optional<Victim> foo; 

     // An error 
     foo.emplace(); 
     /// Assertion is failed 
     static_assert(std::is_constructible<Victim>::value); 
    } 
}; 

żywo przykład na godbolt.org


Zmiana któregokolwiek z warunków i kompiluje, jak oczekiwano. Czy istnieje jakaś nieznana niespójność w standardzie, która sprawia, że ​​klang odrzuca ten kod, będąc jednocześnie zgodny?

marginesie: GCC 7,1 i GCC 7,2 nie ma problemu z powyższym kodu.


raport Bug w: bugs.llvm.org

+0

Może to być błąd kompilatora. –

+0

@CrisLuengo, mam nadzieję, że jest łatwiejsze do naprawienia niż standardowe. – GreenScape

+1

W gruncie rzeczy naprawdę jest to pytanie dotyczące prawnika językowego. Na to należy odpowiedzieć. – StoryTeller

Odpowiedz

7

To wygląda jak błąd kompilatora. Z [class]

klasa jest uważane za w pełni określony typ obiektu (lub całkowite typu) na zamknięciu } do klasy-specyfikacją.

Czyli Victim jest kompletne w std::optional<Victim>, co nie różni się od każdego innego rodzaju w tym kontekście.

Od [meta]

Warunkiem orzeczenie o specjalizacji szablonu is_­constructible<T, Args...> powinien być spełniony wtedy i tylko wtedy, gdy po definicja zmiennej będzie dobrze uformowane przez niektóre wymyślone zmienna t: T t(declval<Args>()...);

Bezpośrednie inicjowanie t z argumentami typu Args..., lub jeśli sizeof...(Args) == 0, to inicjowanie wartości t.

W tym przypadku value-initializing t is to default-initializet, które jest ważne z tego powodu, std::is_constructible_v<Victim>, powinno być prawdziwe.

Z tym wszystkim, kompilatory wydają się być kompilacją struggling a lot.

+1

Z tego samego akapitu: "W specyfikacji klasy członka klasa jest uznawana za kompletną w [...] domyślnych inicjalizatorach członków (włączając w to takie rzeczy w klasach zagnieżdżonych)." Wydaje się to sugerować, że kompilatory nie mogą przetworzyć domyślnych inicjalizatorów elementów, dopóki nie zobaczą zamknięcia '}' (i klas zagnieżdżonych, dopóki nie zobaczą zamknięcia '}' klasy otaczającej). – cpplearner

+2

@cpplearner Wierzę, że odnosi się do faktu, że klasa (otaczająca) jest niekompletna w swoim własnym ciele, a nie to, że jego zagnieżdżone klasy są niekompletne. –

+1

Chodzi mi o to, domyślne inicjatory członków są nadal zupą żetonów (tj.nie są analizowane) w obrębie klasy otaczającej, a inicjowanie agregatu będzie wymagało odwoływania się do tych domyślnych inicjalizatorów elementów. Jest to bardziej oczywiste poza kontekstem SFINAE: 'struct Outer {struct Inner {int x = 4; }; decltype (Inner()) a; }; ' – cpplearner

3

Dobrze, przekopałem odpowiednie cytaty. Kluczową sprawą jest to, w jaki sposób powinien obsłużyć . Najbardziej rozstrzygającym autorytetem jest C++ 17 (n4659).Pierwszy [meta.unary.prop/8]:

Warunkiem orzeczenie o specjalizacji szablonu is_­constructible<T, Args...> powinien być spełniony wtedy i tylko wtedy, gdy następujących definicji zmiennej będzie dobrze uformowane przez niektóre wymyślone zmienną T:

T t(declval<Args>()...); 

[ Uwaga: Żetony te nigdy nie są interpretowane jako deklaracja funkcji. - nota końcowa] Sprawdzanie dostępu odbywa się tak, jakby w kontekście niezwiązanym z T i żadnym z Arg. Uwzględniana jest tylko ważność bezpośredniego kontekstu inicjowania zmiennej.

Nota I podkreślił nie jest normatywna (z tytułu bycia notatka), ale pokrywa się z [temp.variadic]/7:

... Gdy N jest zerem, instancji ekspansji produkuje pusta lista. Takie tworzenie nie zmienia interpretacji syntaktycznej konstruktu obejmującego, nawet w przypadkach, w których pominięcie listy byłoby inaczej źle sformułowane lub spowodowałoby niejednoznaczność gramatyki.

Więc dla celów is_­constructible ten T t(); rzeczywiście sprawia t zmienna deklaracja. Ten inicjalizacji inicjalizacji wartość ponieważ [dcl.init/11] mówi tyle:

Przedmiotem których inicjator jest zbiorem pustym z nawiasach, to znaczy() są wartości inicjalizacji.

Oznacza to, że cecha kończy się sprawdzaniem, czy można zainicjować wartość Victim. Które może. Jest to agregat, ale domyślnie domyślny domyślny c'tor jest nadal definiowany przez kompilator (oczywiście w celu obsługi inicjowania wartości).

Krótka historia. Clang ma błąd, powinieneś go zgłosić.

Powiązane problemy