2015-05-14 4 views
6

Ich habe eine Template-Funktion, die ich nur für Standard-Container (oder Container kompatibel mit Standard-Containern, die mindestens eine begin() Member-Funktion bieten). Ich SFINAE-ing aus Nicht-Container auf folgende Weise:Wie SFINAE out non-Container-Parameter

template<typename Container> 
typename Container::value_type 
f(const Container& c, 
    typename std::enable_if< 
     std::is_same< 
      decltype(*c.begin()), 
      typename Container::value_type 
     >::value 
    >::type* = nullptr) 
{ 
    // implementation here 
} 

Die std::is_same und decltype nicht zu elegant aussehen. Gibt es dafür einen besseren Weg?

PS: Ich brauche die SFINAE hier, weil ich eine andere Überlastung

template<typename Derived> 
f(const Eigen::MatrixBase<Derived>& A) 

haben und wenn ich f(some_Eigen_matrix) versuchen, die Container Überlastung endet abgeholt, dann spuckt der Compiler einen Fehler, weil der Typ fehlt begin().

+3

Warum machst du die Gleichheitsprüfung, überhaupt? Reicht es nicht aus, wenn der Ausdruck gültig ist? – Columbo

+0

'auto f (const Behälter & c) -> decltype (c.begin(), (void) c.end())' –

+0

Ok, es ist ein bisschen chaotischer. Der Rückgabetyp ist eine Summe über die Elemente des Containers. – vsoftco

Antwort

6

Mit void_t können wir nur eine Art Zug machen für begin() und end() mit (und alles, was Sie vielleicht überprüfen möchten, wie typename T::iterator, können Sie einfach auf Anhäufung Ausdrücke halten):

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

template <typename T> 
struct is_std_container<T, 
    void_t<decltype(std::declval<T&>().begin()), 
      decltype(std::declval<T&>().end()), 
      typename T::value_type 
      >> 
    : std::true_type { }; 

Und dann SFINAE nur auf, dass:

template <typename Container> 
typename std::enable_if< 
    is_std_container<Container>::value, 
    typename Container::value_type 
>::type 
f(const Container& c) { .. } 

auch, wenn Sie wirklich wollte, um sicherzustellen, dass begin() Sie wieder ein compa T::iterator (oder zumindest, dass sie Gleichheit gibt), können Sie das auch tun:

void_t< 
    decltype(begin(std::declval<T&>()) == std::declval<typename T::iterator>()) 
> 
+0

Ja, das sieht tatsächlich besser aus, und ich werde wahrscheinlich diesen Weg gehen, da ich diesen Scheck wiederverwende. Vielen Dank! – vsoftco

+0

Der 'void_t'-Trick scheint sehr nett, danke für den Link zu der Zeitung. – vsoftco

+1

Das einzige, was mir entgeht, ist, warum der 'is_std_container ' die 'true_type'-Spezialisierung übernimmt, wenn' Container' tatsächlich ein Container ist? Tut mir leid, wenn die Frage albern erscheint. Ich fand tatsächlich http://stackoverflow.com/questions/27687389/how-does-void-t-work, wird es lesen. Ich denke, die Idee ist, dass eine Matchin Teilspezialisierung immer besser ist. KLUG! – vsoftco