2016-07-16 11 views
0
#include <vector> 

template 
< 
    typename T, 
    typename Alloc, 
    template<typename, typename> class Left 
> 
Left<T, Alloc>&& 
operator <<(Left<T, Alloc>&& coll, T&& value) 
{ 
    coll.push_back(std::forward<T>(value)); 
    return std::forward<Left<T, Alloc>>(coll); 
} 

using namespace std; 

int main() 
{ 
    vector<int> c1; 
    c1 << int(8); 
} 

VS 2015 Ausgänge:Warum funktionieren Vorlagenparameter nicht wie erwartet?

Fehler C2678: binary '< <': kein Operator gefunden, die eine linke nimmt - Hand Operand des Typs 'std :: vector>' (oder gibt es keine akzeptable Umwandlung)

Warum funktionieren Vorlagenparameter nicht wie erwartet?

Antwort

6

Ihre Funktion übernimmt eine rvalue Referenz aber Sie vorbei eine L-Wert - Left<T, Alloc>&& ist nicht eine Weiterleitung Referenz, so dass es als solche Behandlung mit std::forward usw. ist nicht korrekt. Vorerst werden wir Sammlung rvalues ​​nicht zulassen, um die Dinge zu vereinfachen:

template< 
    typename T, 
    typename Alloc, 
    template<typename, typename> class Left 
> 
Left<T, Alloc>& operator <<(Left<T, Alloc>& coll, T&& value) { 
    coll.push_back(std::forward<T>(value)); 
    return coll; 
} 

Die oben einen Schritt näher, aber will not work wenn ein L-Wert für value geben. Eine Möglichkeit ist es, das richtige Argument zu zwingen, für Left:

template< 
    typename T, 
    typename Alloc, 
    template<typename, typename> class Left 
> 
Left<typename std::decay<T>::type, Alloc>& 
operator <<(Left<typename std::decay<T>::type, Alloc>& coll, T&& value) { 
    coll.push_back(std::forward<T>(value)); 
    return coll; 
} 

Online Demo

Dies funktioniert, aber nicht lassen Sie uns eine einfache Möglichkeit Sammlung rvalues ​​zu unterstützen. Die richtige Lösung hier IMO ist mit Template-Vorlagen zu stoppen und entweder static_assert, dass die value_type Matches des Containers T oder SFINAE der Bediener weg, wenn dies nicht der Fall:

template<typename Coll, typename T> 
Coll& operator <<(Coll& coll, T&& value) { 
    static_assert(std::is_same< 
      typename std::decay<T>::type, 
      typename Coll::value_type 
     >::value, 
     "T does not match Coll::value_type" 
    ); 
    coll.push_back(std::forward<T>(value)); 
    return coll; 
} 

Online Demo

oder

template<typename Coll, typename T> 
typename std::enable_if<std::is_same< 
     typename std::decay<T>::type, 
     typename Coll::value_type 
    >::value, 
    Coll& 
>::type 
operator <<(Coll& coll, T&& value) { 
    coll.push_back(std::forward<T>(value)); 
    return coll; 
} 

Online Demo

Mit diesem Schritt, jetzt, wenn Sie entscheiden, dass Sie Sammlung Rvalues ​​schließlich unterstützen möchten, ist dies trivial; die static_assert Implementierung als Beispiel zu verwenden:

template<typename Coll, typename T> 
Coll&& operator <<(Coll&& coll, T&& value) { 
    static_assert(std::is_same< 
      typename std::decay<T>::type, 
      typename std::decay<Coll>::type::value_type 
     >::value, 
     "T does not match Coll::value_type" 
    ); 
    coll.push_back(std::forward<T>(value)); 
    return std::forward<Coll>(coll); 
} 

Online Demo

N. B. die obige Implementierung erlaubt nur die Verwendung des Operators mit genauen Treffer-Coll::value_type, aber es ist wahrscheinlich sinnvoll, etwas zu ermöglichen, die umgewandeltColl::value_type sein kann - dies umzusetzen, ersetzen Sie einfach std::is_same mit std::is_convertible.

+0

Warum ist Links <...> && keine Weiterleitungsreferenz? (Warum wird dies nicht als eine Art deduzierende Situation betrachtet?) – kfsone

+1

@kfsone: Der Standard definiert eine Weiterleitungsreferenz als "eine rvalue-Referenz auf einen cv-unqualifizierten Vorlagenparameter"; Ein spezieller Template-Template-Parameter ist nicht mit einem Template-Parameter identisch. : -] – ildjarn

+0

Danke für die Erklärung. Ich kann sehen, dass dies eine häufige Quelle von Missverständnissen ist – kfsone

Verwandte Themen