2014-10-30 2 views
6

Ich habe folgendes Templat-Objekt:Wie verwende ich std :: enable_if, um Konstruktoren je nach Vorlagentypen zu aktivieren oder zu deaktivieren?

template< typename type_1, typename type_2 > struct result 
{ 
    // I want to enable these two constructors only if type_1 != type_2 
    result(type_1 f) : foo{f} {} 
    result(type_2 b) : bar{b} {} 

    // I want to enable this constructor only if type_1 == type_2 
    result(type_1 f, type_2 b) : foo{f}, bar{b} {} 

    // Other member functions removed. 

    type_1 foo; 
    type_2 bar; 
}; 

Wie verwende ich std::enable_if die Konstrukteure zu aktivieren oder deaktivieren wie gewünscht?

zB:

Dies würde man nur die ersten beiden Konstrukteure haben:

result<string,int> // type_1 != type_2 

Dies würde nur den dritten Konstruktor:

result<int,int> // type_1 == type_2 

Antwort

5

This scheint zu funktionieren, aber ich bin nicht sicher, es ist der optimale Weg

Also einfach hinzufügen neuen Template-Parameter mit Standardwerten an den Konstruktor SFINAE

#include <type_traits> 

template< typename type_1, typename type_2 > 
struct result 
{ 
    // I want to enable these two constructors only if type_1 != type_2 
    template<typename T1 = type_1, typename T2 = type_2> 
    result(type_1 f, 
      typename std::enable_if<!std::is_same<T1, T2>::value>::type * = nullptr) 
     : foo{f} {} 
    template<typename T1 = type_1, typename T2 = type_2> 
    result(type_2 b, 
      typename std::enable_if<!std::is_same<T1, T2>::value, int >::type * = nullptr) 
     : bar{b} {}          /*  ^^^ need this to avoid duplicated signature error with above one*/ 

    // I want to enable this constructor only if type_1 == type_2 
    template<typename T1 = type_1, typename T2 = type_2> 
    result(type_1 f, type_2 b, 
      typename std::enable_if<std::is_same<T1, T2>::value>::type * = nullptr) 
     : foo{f}, bar{b} {} 

    type_1 foo; 
    type_2 bar; 
}; 

int main() 
{ 
    result<int, double> r(1); 
    result<int, double> r2(1.0); 

    result<int, int> r3(1, 2); 

    // disbaled 
    //result<int, double> r4(1, 2.0); 
    //result<int, int> r5(1); 
} 

Lesen Sie auch aktivieren: Select class constructor using enable_if

4

Die primäre Vorlage kann als Spezialisierung für mistmatched Typen dienen. Für passende Typen können Sie teilweise spezialisieren:

template <typename type_1, typename type_2> 
struct result 
{ 
    result(type_1 f) : foo{f} {} 
    result(type_2 b) : bar{b} {} 

    type_1 foo; 
    type_2 bar; 
}; 

template <typename type> 
struct result<type, type> 
{ 
    result(type f, type b) : foo{f}, bar{b} {} 

    type foo; 
    type bar; 
}; 
+1

Ich habe meine Frage aktualisiert, um zu erwähnen, dass das 'result' Objekt eine Reihe von Member-Funktionen hat, die nicht gezeigt werden, so würde Spezialisierung zu viel Code-Duplikation führen. –

+1

@DrTwox Dann Bryan Lösung verwenden. Leider sind mir im Moment keine Alternativen bekannt. – 0x499602D2

+1

@DrTwox Sie können die Vererbung verwenden, um diese Wiederholung zu vermeiden, indem Sie den allgemeinen Code in eine Klasse und den spezialisierten Code in die andere Klasse einfügen. Dies ist eine übliche Lösung. Sie können dies auf verschiedene Arten tun, einschließlich CRTP, abhängig von Ihrer Situation. Beispielsweise könnten Sie die Konstruktoren (und Datenelemente) in einer Basisklasse haben, die Sie für beide Fälle (Typen gleich oder nicht) spezialisieren, und dann geerbte Konstruktoren (C++ 11) in Ihrer abgeleiteten Klasse verwenden. Oder das Gegenteil, spezialisieren Sie eine dünne abgeleitete Klasse mit dem gesamten Code in der Basisklasse. –

2

Dies ist ähnlich @ BryanChen Antwort, aber sauberer IMO :) Sie können die Vererbung verwenden, um die Mehrdeutigkeiten zu verbessern und die enable_if s auf die Vorlage Argumente des Konstruktors bewegen.

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

using namespace std; 

template <int N> 
class Disambiguator; 

template<> 
class Disambiguator<0>{}; 

template <int N> 
class Disambiguator : public Disambiguator<N-1>{}; 

using Disambiguate = Disambiguator<100>; 

template< typename type_1, typename type_2 > struct result 
{ 
    template <typename T, typename U> 
    using IsSame = typename enable_if<is_same<T, U>::value>::type; 

    template <typename T, typename U> 
    using IsNotSame = typename enable_if<!is_same<T, U>::value>::type; 

    template <typename T = type_1, typename U = type_2, typename = IsNotSame<T,U>> 
    result(type_1 f, Disambiguator<0>) : foo{f} {cout<<"NotSameType"<<endl;} 

    template <typename T = type_1, typename U = type_2, typename = IsNotSame<T,U>> 
    result(type_2 b, Disambiguator<1>) : bar{b} {cout<<"NotSameType"<<endl;} 

    // I want to enable this constructor only if type_1 == type_2 
    template <typename T = type_1, typename U = type_2, typename = IsSame<T,U>> 
    result(type_1 f, type_2 b) : foo{f}, bar{b} {cout<<"SameType"<<endl;} 

    // Other member functions removed. 

    type_1 foo; 
    type_2 bar; 
}; 


int main() 
{ 
    result<float, int> c(1.0, Disambiguate{}); 
    result<float, int> i(0, Disambiguate{}); 

    result<int, int> j(0, 0); 

    result<string, int> s("abc", Disambiguate{}); 
    result<string, int> si(0, Disambiguate{}); 

    return 0; 
} 

EDIT: Sie können die Überladungsauflösung Idee Xeo here lesen @. Das habe ich im obigen Code verwendet.

Verwandte Themen