5

Mam kilka typów, które mają podtypy o tej samej nazwie każda:Niestandardowy komunikat o błędzie podczas kompilacji niezdefiniowana podtyp jest dostępne

struct TypeA { 
    typedef int subtype; 
}; 
struct TypeB { 
    typedef float subtype; 
}; 

a także rodzaje, które nie mają tego podtypu ale które są wykorzystywane w tym samym kontekście:

struct TypeC { 
    // (no subtype defined) 
}; 

Jak mogę dodać podtyp atrapę, która daje zwyczaj skompilować komunikat o błędzie?

My (jak dotąd nieskuteczne) próba jest:

struct TypeC { 
    struct subtype { 
     static_assert(false, "Attempt to access the non-existent subtype of TypeC."); 
    }; 
}; 

Ale static_assert(false, ...) nie może działać, ponieważ kompilator zgłasza błąd, nawet jeśli typ nie jest dostępne.

Jak mogę opóźnić ocenę static_assert do czasu, w którym typ jest używany?

nieudanej próbie jest wprowadzenie obojętne enum i skonstruować wyrażenie z niego:

enum { X }; 
static_assert(X != X, "..."); 

przypadek użycia betonu: Mam klasy szablon List który jest zdefiniowany z pod- typy head i tail jeśli niepusty i powinien dać błąd, jeśli używane są te podtypy jeśli jest pusty:

template<typename...> 
struct List; 

// empty list: 
template<> 
struct List<> { 
    struct head { static_assert(false, "Attempt to access the head of an empty list."); }; 
    struct tail { static_assert(false, "Attempt to access the tail of an empty list."); }; 
}; 

// non-empty list: 
template<typename Head, typename ...Tail> 
struct List<Head, Tail...> { 
    typedef Head head; 
    typedef List<Tail...> tail; 
}; 

Jeśli po prostu pomijam typy head i tail, podczas uzyskiwania dostępu, np. 3rd elementem listy, która ma wielkość 2 z kodem List<int,int>::tail::tail::head nie daje tak miły komunikat (g ++ 4.7.2): 'head' is not a member of 'List<int>::tail {aka List<>}'

+0

Ten przykład 'List <>' nie narzeka na 'static_assert's? Myślałem, że ciągła ekspresja musi obejmować parametr szablonu, aby uniknąć natychmiastowej oceny. – aschepler

+0

Hmm, pozorne wyliczenie [wydaje się nie działać] (http://coliru.stacked-crooked.com/a/602ff84bdc70c08e). –

+0

@aschepler Działa z g ++ 4.7.2, nie jest pewien innych kompilatorów czy nawet standardu. – leemes

Odpowiedz

5
// empty list: 
template<typename... Args> 
struct List { 
    struct head {static_assert(sizeof...(Args) != 0, "Attempt to access the head of an empty list."); }; 
    struct tail {static_assert(sizeof...(Args) != 0, "Attempt to access the tail of an empty list."); }; 
}; 

// non-empty list: 
template<typename Head, typename ...Tail> 
struct List<Head, Tail...> { 
    typedef Head head; 
    typedef List<Tail...> tail; 
}; 

Edit: Ten problem rzeczywiście dotyka trzech aspektów, jak działają Szablony C++

  1. (§14.7.1 [temp.inst]/p1) O ile specjalizacja klasa szablon został wyraźnie instancja (14.7.2) lub wyraźnie wyspecjalizowany (14.7.3), specjalizacja szablonu klasy jest niejawnie tworzona, gdy odwołanie do specjalizacji odbywa się w kontekście, który wymaga całkowicie zdefiniowanego typu obiektu lub gdy kompletność typu klasy wpływa na semantykę programu.Niejawna inicjacja specjalizacji szablonu klasy powoduje niejawne tworzenie deklaracji, ale nie definicje funkcji klas, klas członków, [...].
  2. (§14.7.1 [temp.inst]/p11) Implementacja nie powinna domyślnie tworzyć ... klasy elementów ... szablonu klasy, która nie wymaga instancji.
  3. (§ 14.6 [temp.res]/p8) Jeśli dla szablonu nie można wygenerować ważnej specjalizacji, a szablon nie jest utworzony, szablon jest nieprawidłowy, nie jest wymagane żadne diagnostyczne.

3) oznacza, że ​​wyrażenie static_assert musi zależeć od szablonu argumentu, gdyż w przeciwnym razie „nie obowiązuje specjalizacja” mogą być generowane dla szablonu i program jest źle sformułowane, i kompilatory są wolne, aby zgłosić błąd (chociaż nie muszą). W powyższym kodzie można wygenerować prawidłową specjalizację dla pierwszego szablonu, ale taka specjalizacja nigdy nie jest używana z powodu częściowej specjalizacji.

Powyższe rozwiązanie również opiera się na 1) i 2). 1) przewiduje, że w sposób dorozumiany instancji specjalizacja szablonu instancję tylko deklaracje (nie definicje) klas członkowskich, 2) oznacza, że ​​kompilatory są twierdząco zakazane od próby wystąpienia head lub tail jeśli jest jedynie stosując niejawnie instancja List<> . Uwaga: ta reguła nie ma zastosowania, jeśli jawnie utworzono List<> z template struct List<>;.

Rozwiązaniem w odpowiedzi leemes dzieła bo typedef s nie wymagają kompletnego typ i tak nie wywołać niejawny konkretyzacji SubTypeErrorMessage<> pod 1), korzystanie z szablonu argumentu w static_assert w SubTypeErrorMessage omija 3), jako poprawna specjalizacja (tj. SubTypeErrorMessage<true>) może zostać wygenerowana dla tego szablonu.

Warto zauważyć, że w obu przypadkach reguły tworzenia instancji oznaczają, że nadal dozwolone jest używanie List<>::head lub TypeC::subtype, o ile nie używa się ich w sposób wymagający pełnego typu. Tak więc coś takiego jak int f(List<>::head &) { return 0; } jest poprawne, choć zupełnie bez znaczenia, ponieważ nie ma możliwości, aby można było wywołać tę funkcję. Jeśli jednak nie zdefiniujesz w ogóle List<>::head, kompilator zgłosi (prawdopodobnie tajemniczy) błąd tego kodu. To jest kompromis dla ładniejszych komunikatów o błędach :)

+0

Och ładny, w wyrażeniu jest nawet "poprawna logika";) (chociaż przypadki o rozmiarze większym nigdy nie docierają do niego ...) Dziękuję dla tego miłego rozwiązania. – leemes

+0

Dziękuję bardzo za szczegółowe wyjaśnienie. Podsumowując, zarówno twoje, jak i moje rozwiązanie gwarantują działanie, tzn. Kompilator nie może oceniać twierdzenia wcześniej, niż bym chciał. – leemes

+0

@leem Wierzę, że tak. –

5

opóźnić ocenę static_assert do punktu, w którym dany typ jest dostęp, trzeba spraw, aby wyrażenie zależało od parametru szablonu.

Możliwym rozwiązaniem jest dodanie szablonu klasy pomocnika tylko do drukowania komunikat o błędzie warunkowo (w zależności od wartości parametru szablonu):

template<bool X = false> 
struct SubTypeErrorMessage { 
    static_assert(X, "Attempt to access the non-existent subtype of TypeC."); 
}; 

Następnie w betonowej typu, w którym chcesz mieć Określenie "obojętne podtyp":

struct TypeC { 
    typedef SubTypeErrorMessage<> subtype; 
}; 

Live Demo

Powiązane problemy