2017-07-01 2 views
12

Ich schrieb den folgenden Code:Warum wird die Template-Spezialisierung nicht gewählt?

#include <iostream> 
#include <string> 
#include <type_traits> 

template<typename, typename = void> 
struct is_incrementable : std::false_type {}; 

template<typename T> 
struct is_incrementable<T, decltype(++std::declval<T&>())> : std::true_type {}; 

int main() 
{ 
    std::cout << is_incrementable<std::string>::value << std::endl; 
    std::cout << is_incrementable<int>::value << std::endl; 
} 

Wenn ich es laufen, ich 0 0 bekommen. Aber ich erwartete 0 1.

Irgendwelche Ideen?

+2

Dies ist der Ort für [std :: void_t] (http://ideone.com/QUJxId). – DeiDei

+0

Darüber hinaus haben die Dokumente für void_t genau diese Art von Code als Beispiel, siehe http://en.cppreference.com/w/cpp/types/void_t Code für 'has_pre_increment_member' – stijn

Antwort

20

Für std::string wird die primäre Vorlage ausgewählt und die Spezialisierung berücksichtigt. Aber decltype(++std::declval<T&>()) ist schlecht gebildet, so dass es nicht berücksichtigt wird und die primäre Vorlage (nicht spezialisierte Vorlage) verwendet wird, die 0 ergibt.

Wenn Sie int verwenden, wird es ein bisschen komplizierter. Die primäre Vorlage wird wie immer vom Compiler ausgewählt, und dann wird die Spezialisierung berücksichtigt (weil Spezialisierung immer als bessere Übereinstimmung betrachtet wird). Die Spezialisierung ist <int, int&> für int, aber es stimmt nicht mit der nicht spezialisierten Vorlage <int, void> überein (das void ist das Standardschablonenargument), daher wird die Spezialisierung ignoriert, da sie nicht übereinstimmt.

Also müssen die Typen des Standard-Template-Parameters übereinstimmen, sonst wird die Spezialisierung nicht berücksichtigt, da eine Spezialisierung nur dann erfolgt, wenn jedes Template-Argument der Spezialisierung entspricht.

hängen Sie einfach ein void() am Ende die Spezialisierung Spiel für die zweiten Template-Parameter zu machen, wie die linken verworfen und die Art der void() ist void, die die primäre Vorlage der zweiten Template-Parameter übereinstimmt.

template<typename T> 
struct is_incrementable<T, decltype(++std::declval<T&>(), void())> : std::true_type {}; 

In C++ 17, würden Sie std::void_t dafür.

+0

Wenn ich' template struct is_incrementable: std :: false_type {}; 'es hat auch nicht funktioniert. Kannst du das auch erklären? Vielen Dank! – Klaus

+2

@Klaus Ein nicht überladener Pre-Inkrement-Operator gibt das Objekt selbst zurück, also ist 'declltype (++ i)' für 'int i;' ein 'int &' und kein 'int'. Wenn Sie 'typename = int &' verwenden, wird auch '0 1' ausgegeben. – Rakete1111

Verwandte Themen