2012-11-26 9 views
8

Wenn ich eine Vorlage haben, die einen Standard-Container-Wraps, so scheint es, ich ziemlich leicht den initializer_list Konstruktor delegieren kann:Optional initializer_list Konstruktion für Vorlagen vielleicht Einwickeln Container unterstützt

template<typename T> 
struct holder { 
    T t_; 

    holder() : 
     t_() {} 

    holder(std::initializer_list<typename T::value_type> values) 
     : t_(values) {} 

}; 

Also das funktioniert gut mit std :: vector , zum Beispiel.

int main(int argc, char* argv[]) { 

    holder<std::vector<int>> y{1,2,3}; 
    return EXIT_SUCCESS; 
} 

Aber es ist ziemlich offensichtlich funktioniert nicht für T als ‚int‘, oder jede andere Art, die nicht eine verschachtelte value_type typedef hat. Daher möchte ich eine Art enable_if oder einen ähnlichen Trick verwenden, damit der Konstruktor initializer_list nicht ausgegeben wird, es sei denn, T both definiert einen verschachtelten value_type typedef und kann aus std :: initializer_list konstruiert werden.

habe ich versucht, die folgenden, aber es immer noch nicht funktioniert, weil der Compiler (Klirren ++ 3.1 in meinem Fall), noch der ungültigen T :: value_type stolpert über, wenn T int ist: Jede

holder(typename std::enable_if<std::is_constructible<T, std::initializer_list<typename T::value_type>>::value, std::initializer_list<typename T::value_type>>::type values) 
    : t_(values) {} 

Gedanken darüber, wie man das Konzept ausdrücken kann: "Gib diesem Template auf T einen Initialisiererlistenkonstruktor über T's value_type, wenn und nur wenn T einen value_type typedef hat und aus einer initializer_list von T :: value_type konstruiert werden kann.

+0

Ist 'initializer_list' Wert übergeben richtig, oder sollte sie durch eine Referenz-Typ übergeben werden? ('&&'?) –

+0

@MartinBa: pass by value ist korrekt (und empfohlen) für 'std :: initializer_list'. –

+0

Eine 'initializer_list' enthält nur einen Zeiger und eine Größe, also ist es in Ordnung, sie als Wert zu übergeben. Die Daten selbst werden nicht kopiert. –

Antwort

4

SFINAE funktioniert nur bei Template-Parameter-Substitution (daher das S in SFINAE). Die folgenden Werke:

template<typename T> 
struct holder { 
    T t_; 

    holder() : 
     t_() {} 

    template<typename U = T> 
    holder(typename std::enable_if<std::is_constructible<U, std::initializer_list<typename U::value_type>>::value, 
    std::initializer_list<typename U::value_type>>::type values) 
    : t_(values) {} 

}; 

Wenn Sie keine Template-Funktion, dann die ganze Klasse wäre für den Typen int (in Ihrem Beispiel) instanziiert werden, was zu einem Compiler-Fehler verwendet haben.

Beachten Sie, dass die Funktion Signatur besser aus, wenn Sie ein zusätzliches Template-Parameter verwendet machen könnte:

template<typename U = T, class = typename std::enable_if<std::is_constructible<U, std::initializer_list<typename U::value_type>>::value, bool>::type> 
    holder(std::initializer_list<typename U::value_type> values) 
    : t_(values) {} 
+0

Interessant, aber es funktioniert nicht ganz. Mit meiner ursprünglichen Version 'Halter > y = {1, 2, 3}' funktioniert richtig, aber zumindest mit meinem Compiler funktioniert es nicht mit Ihrer überarbeiteten Konstruktorsignatur. Es beschwert sich, dass "Kandidat Konstruktor Vorlage nicht durchführbar: benötigt 1 Argument, aber 3 wurden bereitgestellt" – acm

+2

Könnten Sie nicht die enable_if fallen lassen und einfach 'template Halter (std :: initializer_list Werte) '? –

+0

@VaughnCato scheint das besser zu funktionieren. – acm