2015-08-14 15 views
8

Ich möchte eine has_no_duplicates<...> Typeigenschaft implementieren, die std::true_type auswertet, wenn die übergebene variadic Typenliste keine doppelten Typen hat.Verwenden von `void_t`, um mehrfache Vererbungstypwiederholungsfehler zu erkennen

static_assert(has_no_duplicates<int, float>{}, ""); 
static_assert(!has_no_duplicates<float, float>{}, ""); 

Nehmen wir an, für den Anwendungsbereich dieser Frage, dass ich das mit Mehrfachvererbung tun wollen.

Wenn eine Klasse aus der gleichen Art mehr als einmal erbt, tritt ein Fehler auf.

template<class T> 
struct type { }; 

template<class... Ts> 
struct dup_helper : type<Ts>... { }; 

// No errors, compiles properly. 
dup_helper<int, float> ok{}; 

// Compile-time error: 
// base class 'type<float>' specified more than once as a direct base class 
dup_helper<float, float> error{}; 

Ich nahm an, ich void_t diese Fehler verwendet haben könnte „erkennen“, aber ich konnte nicht eine funktionierende Lösung following the code samples from cppreference implementieren.

Dies ist, was ich versucht:

template<class, class = void> 
struct is_valid 
    : std::false_type { }; 

// First try: 
template<class T> 
struct is_valid<T, std::void_t<decltype(T{})>> 
    : std::true_type { }; 

// Second try: 
template<class T> 
struct is_valid<T, std::void_t<T>> 
    : std::true_type { }; 

Für meinen dritten Versuch habe ich versucht, die Erweiterung der dup_helper<...> mit einem Wrapper-Klasse zu verzögern, die dup_helper als Vorlage Template-Parameter hat, wie wrapper<dup_helper, ...> und erweitert es innerhalb von void_t .

Leider führte alle meine Versuche in den oben genannten Fehler immer Kompilierung verhindert.

Ich nehme an, diese Art von Fehler ist nicht als "Substitutionsfehler" erkennbar, aber ich möchte eine Bestätigung.


Ist diese Art von Fehler tatsächlich nicht mit void_t zu erkennen? (Wird es immer zu einem Kompilierungsfehler führen?)

Gibt es eine Möglichkeit, es zu erkennen, ohne dass die Kompilierung fehlschlägt? (Oder eine nicht void_t Abhilfe, die immer noch die Verwendung des „Mehrfachvererbung Tricks“ macht)?

+1

Ich glaube nicht, dass Sie, dass die Arbeit mit Mehrfachvererbung machen. Das Problem ist, dass es nicht die Deklaration von 'dup_helper ' ist, die einen Fehler verursacht, sondern seine Definition (wenn ich mich nicht irre). – Caninonos

+0

@Caninonos Theoretisch wurde Vittorio ein 'T' in' declltype (T {}) 'instanziiert, um dann den Typ zu erhalten. Vittorio hatte gehofft, dass das 'T {}' einen Fehler erzeugen würde, weil die tatsächliche Anweisung 'T {}' ein Fehler ist, selbst wenn der resultierende Typ nicht ist. – Yakk

+0

@Yakk aber 'T {}' erfordert, dass der Compiler 'T' definiert und der Fehler innerhalb seiner Definition liegt. Es ist ein bisschen so, als würde man versuchen, einen Fehler im Textkörper einer Template-Funktion zu finden (statt dessen hinter dem Rückgabetyp/noexcept specifier/default template argument) und es mit sfinae zu "fangen", ich glaube nicht, dass es möglich ist (Ich weiß nicht genau, was der Standard sagt, aber immer noch). (Es gibt auch [this] (http://ideone.com/oT20iI), um eine "Lösung" mit Mehrfachvererbung zu posten. Es ist jedoch völlig unzuverlässig, da es auf EBO und der Größe einer leeren Klasse beruht. t use it) – Caninonos

Antwort

6

Wie @Canoninos erwähnt, ist das Problem, dass:

es nicht die Deklaration von dup_helper<T, T> ist, die einen Fehler, aber seine Definition bewirkt, dass [...].

Oder in Standardese, der Fehler auftritt, außerhalb des "unmittelbaren Kontext" ([temp.deduct]) der Substitution:

8 - [...] Nur ungültigen Typen und Ausdrücke im unmittelbaren Kontext des Funktionstyps und seine Vorlage Parametertypen können zu einem Abzug Fehler führen. [Anmerkung: Die Auswertung der substituierten Typen und Ausdrücke können zu Nebenwirkungen führen, wie beispielsweise die Instanziierung der Klasse Vorlage Spezialisierungen und/oder Funktionsschablone Spezialisierungen, die Erzeugung von implizit definierten Funktionen usw. Solche Nebenwirkungen sind in dem „unmittelbaren Kontext“ nicht und schlecht ausgebildet in dem Programm wird zur Folge haben kann.- Endnote]

Hier tritt der Fehler während Instanziierungdup_helper<float, float> so nicht in der "unmittelbaren Kontext" ist.

Ein Mehrfachvererbung Trick, der ganz in der Nähe ist, um Ihnen eine Extraschicht Vererbung beinhaltet das Hinzufügen, durch die mehrere Basen Indizierung:

helper<<0, 1>, <float, float>>   
      +    
     +----+----+  
     v   v  
ix<0, float> ix<1, float> 
     +   +  
     v   v  
    t<float> t<float> 

Dies gibt uns eine Hilfsklasse mit einer gültigen Definition und instanziiert werden kann, aber wirft nicht zu seinen endgültigen Basisklassen, weil die Mehrdeutigkeit:

static_cast<t<float>>(helper<...>{}); // Error, SFINAE-usable 

Example.

+0

Es gibt keine Es scheint, dass gute "sofortige Kontext" -Primer im Internet (außerhalb von Kopien des Standards) zu sein scheinen. Haben Sie eine Entschlüsselung des Standards, die erklärt, warum dies "außerhalb des unmittelbaren Kontexts" ist? – Yakk

+1

@Yakk Meine Faustregel ist, dass wenn Sie auf eine Operation auf Sprachenebene innerhalb des SFINAE-Ausdrucks zeigen können, deren Operanden wohlgeformt sind, die aber trotzdem ungültig ist, dann sind Sie in Ordnung; Wenn die ungültige Operation auf Sprachenebene außerhalb des SFINAE-Ausdrucks liegt, funktioniert sie nicht. – ecatmur

+0

'std :: void_t > (erben ...> {})))' ' Was ist' void (...) 'tun dort das Wrapping der Ganze Sache in eine Funktionssignatur, wenn void_t sowieso variadisch ist? – brunocodutra

0

Das ist meine Lösung mit Meta-Programmierung und Typ-Liste Idiom. Ich verwende diesen Code als Teil meiner Bibliothek, die Reflektion für C++ implementiert. Ich denke, es gibt keine Notwendigkeit in void_t oder Vererbung, diese Aufgabe zu lösen.

template <typename ...Args> 
struct type_list 
{}; 

using empty_list = type_list<>; 

// identity 
template<typename T> 
struct identity 
{ 
    using type = T; 
}; 

// is_typelist 
template<typename T> 
struct is_typelist: std::false_type 
{}; 

template<typename ...Args> 
struct is_typelist<type_list<Args...>>: std::true_type 
{}; 

template<typename T> 
struct check_typelist 
{ 
    using type = void; 
    static constexpr void *value = nullptr; 
    static_assert(is_typelist<T>::value, "T is not a type_list!"); 
}; 

// indexof 
namespace internal { 

template<typename T, typename V, std::int64_t index> 
struct typelist_indexof_helper: check_typelist<T> 
{}; 

template<typename H, typename ...T, typename V, std::int64_t index> 
struct typelist_indexof_helper<type_list<H, T...>, V, index>: 
     std::conditional<std::is_same<H, V>::value, 
      std::integral_constant<std::int64_t, index>, 
      typelist_indexof_helper<type_list<T...>, V, index + 1> 
     >::type 
{}; 

template<typename V, std::int64_t index> 
struct typelist_indexof_helper<empty_list, V, index>: std::integral_constant<std::int64_t, -1> 
{}; 

} 

template<typename T, typename V> 
using typelist_indexof = ::internal::typelist_indexof_helper<T, V, 0>; 

template<typename T, typename V> 
struct typelist_exists: std::integral_constant<bool, typelist_indexof<T, V>::value >= 0> 
{}; 

// remove_duplicates 
namespace internal { 

template<typename P, typename T> 
struct typelist_remove_duplicates_helper: check_typelist<T> 
{}; 

template<typename ...P, typename H, typename ...T> 
struct typelist_remove_duplicates_helper<type_list<P...>, type_list<H, T...>>: 
     std::conditional<typelist_exists<type_list<T...>, H>::value, 
      typelist_remove_duplicates_helper<type_list<P...>, type_list<T...>>, 
      typelist_remove_duplicates_helper<type_list<P..., H>, type_list<T...>> 
     >::type 
{}; 

template<typename ...P> 
struct typelist_remove_duplicates_helper<type_list<P...>, empty_list>: identity<type_list<P...>> 
{}; 

} 

template<typename T> 
using typelist_remove_duplicates = ::internal::typelist_remove_duplicates_helper<empty_list, T>; 


template<typename ...Args> 
struct has_no_duplicates: std::integral_constant<bool, std::is_same<type_list<Args...>, 
                    typename typelist_remove_duplicates<type_list<Args...>>::type>::value> 
{}; 

DEMO

Verwandte Themen