2017-05-18 1 views
0

EDIT: this andere Frage von mir konzentriert sich auf eine reduzierte Version dieses Problems, möglicherweise einfacher zu verstehen.detection idiom: Warum muss die Bedingung eine `using` Richtlinie sein?

Ich schrieb ein kleines Snippet, das das Verhalten von std::experimental::is_detected (here) reproduziert. Meine Implementierung wurde im Wesentlichen von cppreference übernommen, aber ich habe den Template-Parameter Default losgeworden.

Meine Frage ist: im folgenden Ausschnitt, warum has_type (die Bedingung geprüft werden) eine using Erklärung zu sein und kann nicht sein, z.B. eine Struktur (in diesem Fall gibt is_detected ein falsches Ergebnis zurück)?

/***** is_detected definition *****/ 
template<typename...Args> 
using void_t = void; 

template<typename Void, template<class...> class Op, typename ...Args> 
struct Detector { 
    static constexpr bool value = false; 
}; 

template<template<class ...> class Op, typename ...Args> 
struct Detector<void_t<Op<Args...>>, Op, Args...> { 
    static constexpr bool value = true; 
}; 

template<template<class...> class Op, typename...Args> 
using is_detected_t = Detector<void, Op, Args...>; 
/****************************/ 

/***** is_detected test *****/ 
// two dummy types on which to test a condition 
struct Yes { using type = void; }; 
struct No { }; 

// the condition to test 
template<typename T> 
using has_type = typename T::type; 
// struct has_type { using type = typename T::type; }; // does not work as intended! 

int main() { 
    static_assert(is_detected_t<has_type, Yes>::value, ""); 
    static_assert(!is_detected_t<has_type, No>::value, ""); 
    return 0; 
} 
+1

Wenn Sie eine Struktur wollten, müssten Sie 'has_type :: type' anstelle von' has_type' verwenden, damit es funktioniert. – SingerOfTheFall

+0

nicht sicher Ich verstehe ... 'is_detected_t :: value' wäre kein gültiger Ausdruck – blue

Antwort

1

Es könnte durch den Detektor an, wie has_type aussehen helfen tatsächlich verwendet:

template<template<class ...> class Op, typename ...Args> 
struct Detector<void_t< Op<Args...>>, Op, Args...> { 
//      ^^ ^^ 
//     has_type Yes/No 
    static constexpr bool value = true; 
}; 

Für diese Spezialisierung, den Compiler passen sicher, dass Op<Args...> machen müssen, wenn das Ersetzen der Parameter (Op und Args...) mit den tatsächlichen Argumenten (has_type und Yes/No), müssen einen Typ benennen (da das ist was die Vorlage void_t als erstes Template-Argument benötigt).

Da has_type kein Typ ist, sondern eher ein Alias ​​ von einem Typ, muss es aussehen, ob Aliasnamen einen Typ Aliasnamen ist.

Für Yes wird dies Yes::type sein, was wiederum ein Alias ​​von void ist. void ist ein Typ, so ist alles in Ordnung, die Spezialisierung übereinstimmt, value ist true.

Für No wird dieser No::type sein, die es nicht gibt (No hat kein Mitglied type nachdem alle). Daher schlägt die Substitution fehl (aber dies ist kein Fehler,), die Spezialisierung kann nicht verwendet werden. Daher wählt der Compiler die Basisvorlage, wobei valuefalse ist.


Nun, was passiert, wenn Sie has_type wie folgt definieren:

template<typename T> 
struct has_type { using type = typename T::type; } 

dann über Spezialisierung Bedürfnisse (im No Fall), dass ein Typ has_type<No> existiert. has_type ist eine Klassenvorlage, die bei einem gegebenen Typ (No ist ein Typ, also alles gut) einen Typ "erzeugt". Somit ist has_type<No>ein Typ. Somit entspricht die Spezialisierung valuetrue.

Die Mitglieder von has_type<No> sind an dieser Stelle nicht benötigt. Sie könnten sogar template<typename> struct has_type; (nur eine Deklaration, keine Definition) verwenden.Mit anderen Worten, es kann ein unvollständiger Typ sein:

Ein Template-Argument für eine Art Template-Parameter einen Typ-ID sein muss, die einen unvollständigen Typ nennen kann [..]

http://en.cppreference.com/w/cpp/language/template_parameters

Der Inhalt spielt nur dann eine Rolle, wenn der Compiler sie tatsächlich benötigt, z zum Erzeugen eines Objekts dieses Typs:

Dieser Mechanismus wird oft für "type tags" verwendet, die z.B. kann verwendet werden, um verschiedene Arten mit identischem „Inhalt“ aus einer einzigen Vorlage zu erstellen:

template<typename /* TAG */, typename ValueType> 
struct value_of_strong_type { 
    ValueType value; 
    // ... 
}; 

struct A_tag; // no definition 
using A = value_of_strong_type<A_tag, int>; 
struct B_tag; // no definition 
using B = value_of_strong_type<B_tag, int>; 

Beide A und B identisch verhalten, ist aber nicht miteinander konvertierbar, weil sie völlig unterschiedliche Typen ist.


Um den Detektor Arbeit mit einer solchen Klasse-Vorlagen zu machen, wie Sie Sie die folgende Spezialisierung benötigen zeigte:

template<template<class ...> class Op, typename ...Args> 
struct Detector<void_t<typename Op<Args...>::type>, Op, Args...> { 
//      ^^^^^^^^   ^^^^^^ 
    static constexpr bool value = true; 
}; 

Auch wenn Sie es nicht nur hinzufügen können, sonst laufen Sie in mehrdeutige Auflösungsfehler.

Verwandte Themen