2016-06-10 16 views
3

Ich möchte feststellen, ob etwas ein Container ist. Im Moment teste ich es an Containern, die einen Typ und einen Zuweiser haben. Mein ultimatives Ziel ist es, zu lernen, wie man eine Containervorlage schreibt, die, wenn sie Layer von sich selbst enthält, eine direkte Iteration ihrer innersten Elemente erlaubt. Z.B. Wenn es einen Container mit 3 Containern gibt, von denen jeder 3 weitere Container enthält, von denen jeder 3 Elemente enthält, möchte ich in der Lage sein, alle 27 Elemente in einem Bereich basierend auf der Schleife zu durchlaufen. Unterscheidend, ob etwas ein Container ist, der direkt Elemente enthält oder Container von Elementen (oder Containern von Containern ... von Containern von Elementen enthält), die SFINAE verwenden, um zu überprüfen, ob das Element einen Iterator hat, scheint ein logischer erster Schritt in Richtung der begin() Funktion zu sein sollte den Iterator an das Element oder das Ergebnis der begin() Funktion dieses Elements zurückgeben. Wenn ich meinen ersten Schritt arbeiten kann, würde Ich mag diese Logik enthalten in meinem Container zu schreiben, aber ich kann es nicht kompilieren erhalten:Wie kann man feststellen, ob etwas ein Container ist?

#include <vector> 
#include <iostream> 

template <typename T, 
    template <typename E, typename Allocator = std::allocator<E>> class Container 
> 
void is_cont(typename Container<T>::iterator it) 
{ 
    std::cout << "is iterator\n"; 
} 

template <typename T, 
    template <typename E, typename Allocator = std::allocator<E>> class Container 
> 
void is_cont(Container<T>& cont) 
{ 
    std::cout << "is container\n"; 
} 

int main() 
{ 
    std::vector<int> vec{ 2, 4, 6 }; 
    is_cont(vec);    // Output: "is container" 
    //is_cont(vec.begin());  // COMPILER ERROR 
} 

Wie kann ich dieses Problem beheben?

+1

der Iterator Bei 'vec.begin()' (was recht ist wahrscheinlich ist es nur ein 'int *'), es gibt keine Möglichkeit, dass der Compiler jede mögliche Container-Klasse instanziieren kann, um zu sehen, ob dieser Iterator zufällig vom Typ Container :: iterator ist. Und selbst wenn es möglich wäre, wäre es wahrscheinlich mehrdeutig. Sie müssten die Template-Parameter für die Funktion explizit angeben. – Kundor

+0

@Kundor Wenn 'vec.begin()' ein 'int *' ist, wäre da noch ein 'std :: vector :: iterator', mit dem Unterschied, dass es ein' typedef' für 'int * ist '? Es scheint die Zeile 'std :: vector vec {2, 4, 6} zu ersetzen;' mit 'std :: vector vec (3, Balken {});' wo 'Bar' ein leerer Dummy' struct' ist der gleiche Fehler. – CodeBricks

+1

Ja. Damit? Bei einem Funktionsaufruf 'is_cont (int *)' soll der Compiler die Template-Parameter ableiten. Sie fragen also, dass Sie rückwärts von 'int *' arbeiten, um einen Typ mit einem Member typedef 'iterator' zu finden, der' int * 'ist. Sicherlich können Sie sehen, dass dies nicht funktionieren kann (zum einen, da dies Vorlagenklassen sind, gibt es buchstäblich unendliche Möglichkeiten, die es untersuchen müsste). – Kundor

Antwort

5

Ihre Definition, ob etwas ein Container ist oder nicht, basiert ausschließlich darauf, ob er eine iterator innere Klasse hat.

Das ist so gut wie eine Heuristik, nehme ich an, aber lassen Sie uns damit gehen.

In den meisten Fällen ist SFINAE viel einfacher, wenn es mit Klassen gemacht wird, anstatt zu versuchen, SFINAE mit einer Funktion zu implementieren, wie Ihre is_container(). Machen Sie Ihre is_container() in eine Klasse führt zu einer folgenden Lehrbuch SFINAE Lösung:

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

template<typename ...> 
using void_t=void; 

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

template<typename T> 
struct is_container<T, void_t<typename T::iterator>> : std::true_type {}; 

int main() 
{ 
    std::cout << is_container<int>::value << std::endl; 

    std::cout << is_container<std::vector<int>>::value << std::endl; 
    return 0; 
} 

Nun wäre eine damit verbundene Frage, ob sein gibt es eine bessere Art und Weise zu überprüfen, ob etwas ein Behälter ist, oder nicht. Unabhängig davon, welche Heuristik Sie wählen, es ist trivial, einen klassenbasierten Test anstelle eines funktionsbasierten Tests anzupassen. Zum Beispiel, wenn Sie zu prüfen, ob etwas ein Behälter ist, wenn es beide iterator und const_iterator inneren Klassen hat, muss nur eine Zeile ändern:

template<typename T> 
struct is_container<T, void_t<typename T::iterator, 
           typename T::const_iterator>> : std::true_type {}; 
Verwandte Themen