2015-09-16 8 views
7

Ich habe einen Funktor, der auf einen Behälter des Typs wie so U von Elementen des T Typs arbeitetC++ bestimmen, ob ein Behälter :: finden hat()

template<typename T, template<typename...> class U> 
class asserter 
{ 
public: 
    asserter(U<T> &c) : container(c) { }; 
    void operator()(T lhs) 
    { 
     CU_ASSERT(container.find(lhs) != container.end()); 
    }; 
private: 
    U<T> &container; 
}; 

, die ich als

std::set<std::string> a, c; 
... 
asserter<std::string, std::set> ass(c); 
for_each(a.begin(), a.end(), ass); 
verwenden könnte

Wo wir std::includes() für den Moment ignorieren.

Das funktioniert gut, wenn der Container U::find() definiert ist. Wenn nicht, würde ich gerne auf std::find() zurückgreifen. Auf der anderen Seite würde ich lieber U::find() über std::find() verwenden, wenn es verfügbar ist.

In C++ 11 (oder 17 bei Bedarf) kann ich feststellen, ob U::find() verfügbar ist (möglicherweise Beschränkung auf die STL) für U und wenn ja, verwenden Sie sonst std::find()?

Antwort

7

SFINAE, ob der Ausdruck c.find(value) wohlgeformt ist. Trailing-Rückgabetyp ist C++ 11 und nicht unbedingt erforderlich; es macht nur den Rückgabetyp einfacher zu schreiben - decltype(c.find(value)) statt decltype(std::declval<Container&>().find(std::declval<const T&>())).

Wenn der Ausdruck schlecht formatiert ist, wird die erste Überladung von find_impl aus der Überladungsmenge entfernt, so dass die zweite Überlast als einzige lebensfähige übrig bleibt. Der übliche Trick für den dritten Parameter int/long/0 macht die erste Überladung bevorzugt, wenn beide realisierbar sind.

template<class Container, class T> 
auto find_impl(Container& c, const T& value, int) -> decltype(c.find(value)){ 
    return c.find(value); 
} 

template<class Container, class T> 
auto find_impl(Container& c, const T& value, long) -> decltype(std::begin(c)){ 
    return std::find(std::begin(c), std::end(c), value); 
} 

template<class Container, class T> 
auto find(Container& c, const T& value) -> decltype(find_impl(c, value, 0)) { 
    return find_impl(c, value, 0); 
} 

Die übliche Erklärung gilt: Das beruht auf Expression SFINAE, die derzeit nicht von MSVC unterstützt wird; Microsoft plant, Unterstützung in einem Update zu MSVC 2015 hinzuzufügen.

+4

Eine nützlichere Antwort würde auch erklären, warum der Code funktioniert. Zum Beispiel habe ich eine Antwort erwartet, die SFINAE verwendet. Ist der 'declltype (...)' Teil eine SFINAE Technik in C++ 17 (ich denke diese Lambda-Ausdruckssyntax ist C++ 17)? Außerdem wird "int" verwendet, um zu erzwingen, dass diese Signatur Vorrang vor der "langen" Überladung hat? –

+0

Ich habe eine Sorge, dass 'std :: find' für Gleichheit ist, während' std :: set :: find' etc für Äquivalenz ist. Müssen wir darüber nachdenken? –

+0

@NickyC Ich weiß das * theoretisch * sie sind anders, aber wie oft ist das in der realen Welt der Fall? Ich denke, 'set' und' map' (und ihre 'Multi'-Cousins) sind die einzigen, in denen es darauf ankommt, 'unordered_set' und' unordered_map' sollten Gleichheit verwenden. –

Verwandte Themen