
Xander Tulpe
Ich möchte eine Vorlage schreiben, die bestimmt, ob ein Typ zur Kompilierzeit ein STL-Container ist.
Ich habe das folgende Stück Code:
struct is_cont{};
struct not_cont{};
template <typename T>
struct is_cont { typedef not_cont result_t; };
aber ich bin mir nicht sicher, wie ich die notwendigen Spezialisierungen für erstellen soll std::vector<T,Alloc>, deque<T,Alloc>, set<T,Alloc,Comp>
etc…

Nawaz
Notiz: Der folgende Code stammt von einem ausgezeichneten Dienstprogramm namens hübscher Druck geschrieben von @Kerrek SB (ein Thema dazu bei stackoverflow).
Haftungsausschluss: Ich weiß nicht, ob ich diesen Code kopieren und hier einfügen darf, ohne die Erlaubnis des ursprünglichen Autors einzuholen. @Kerrek, lass es mich wissen, wenn du ein Problem hast. 🙂
Sie können diese Klassenvorlage verwenden:
template<typename T>
struct is_container : std::integral_constant<bool, has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value>
{ };
Verwendungszweck:
std::cout << is_container<std::vector<int>>::value << std::endl; //true
std::cout << is_container<std::list<int>>::value << std::endl; //true
std::cout << is_container<std::map<int>>::value << std::endl; //true
std::cout << is_container<std::set<int>>::value << std::endl; //true
std::cout << is_container<int>::value << std::endl; //false
Beachten Sie, dass is_container
benötigt folgende Hilfsklassenvorlagen:
template<typename T>
struct has_const_iterator
{
private:
typedef char yes;
typedef struct { char array[2]; } no;
template<typename C> static yes test(typename C::const_iterator*);
template<typename C> static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
typedef T type;
};
template <typename T>
struct has_begin_end
{
template<typename C> static char (&f(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)),
typename C::const_iterator(C::*)() const>::value, void>::type*))[1];
template<typename C> static char (&f(...))[2];
template<typename C> static char (&g(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)),
typename C::const_iterator(C::*)() const>::value, void>::type*))[1];
template<typename C> static char (&g(...))[2];
static bool const beg_value = sizeof(f<T>(0)) == 1;
static bool const end_value = sizeof(g<T>(0)) == 1;
};

Vaughn Cato
Zuerst definieren Sie Ihre primäre Vorlage, die ein Mitglied hat, das im Standardfall falsch ist:
template <typename T>
struct is_cont {
static const bool value = false;
};
Dann definieren Sie partielle Spezialisierungen für Ihre Containertypen, die stattdessen den Wert true haben:
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};
Verwenden Sie dann für einen Typ X, den Sie überprüfen möchten, ihn wie
if (is_cont<X>::value) { ... }
Verfolgen Sie den Vorschlag, dass ein generischer Kompilierzeittest für
hat-eine-stl-containerähnliche-schnittstelle wäre eine sinnvolle lösung, diese definiert man an stl-ähnlicher Container T
über die Schnittstelle:
T::iterator T::begin();
T::iterator T::end();
T::const_iterator T::begin() const;
T::const_iterator T::end() const;
*T::iterator is T::value_type &
*T::const_iterator is T::value_type const &
Zusätzliche Anforderungen, zB a size()
-Methode auf offensichtliche Weise hinzugefügt werden, oder andere kanonische Typschnittstellen, die zur Kompilierzeit auf offensichtlich ähnliche Weise untersucht werden könnten.
#ifndef IS_STL_CONTAINER_LIKE_H
#define IS_STL_CONTAINER_LIKE_H
#include <type_traits>
template<typename T>
struct is_stl_container_like
{
typedef typename std::remove_const<T>::type test_type;
template<typename A>
static constexpr bool test(
A * pt,
A const * cpt = nullptr,
decltype(pt->begin()) * = nullptr,
decltype(pt->end()) * = nullptr,
decltype(cpt->begin()) * = nullptr,
decltype(cpt->end()) * = nullptr,
typename A::iterator * pi = nullptr,
typename A::const_iterator * pci = nullptr,
typename A::value_type * pv = nullptr) {
typedef typename A::iterator iterator;
typedef typename A::const_iterator const_iterator;
typedef typename A::value_type value_type;
return std::is_same<decltype(pt->begin()),iterator>::value &&
std::is_same<decltype(pt->end()),iterator>::value &&
std::is_same<decltype(cpt->begin()),const_iterator>::value &&
std::is_same<decltype(cpt->end()),const_iterator>::value &&
std::is_same<decltype(**pi),value_type &>::value &&
std::is_same<decltype(**pci),value_type const &>::value;
}
template<typename A>
static constexpr bool test(...) {
return false;
}
static const bool value = test<test_type>(nullptr);
};
#endif
Hier ist ein Testprogramm, erstellt mit GCC 4.7.2, Clang 3.2, Intel C++ 13.1.1:
#include "is_stl_container_like.h"
// Testing ...
#include <iostream>
#include <vector>
#include <array>
#include <functional>
using namespace std;
template<class C>
struct polymorphic : private C
{
typedef typename C::value_type value_type;
typedef typename C::iterator iterator;
typedef typename C::const_iterator const_iterator;
virtual ~polymorphic(){}
virtual const_iterator begin() const {
return C::begin();
}
virtual iterator begin() {
return C::begin();
}
virtual const_iterator end() const {
return C::end();
}
virtual iterator end() {
return C::end();
}
};
template<class C>
struct reject : private C
{
typedef typename C::value_type value_type;
typedef typename C::iterator iterator;
typedef typename C::const_iterator const_iterator;
const_iterator begin() {
return C::begin();
}
iterator begin() const {
return C::begin();
}
const_iterator end() {
return C::end();
}
iterator end() const {
return C::end();
}
};
int main()
{
cout << is_stl_container_like<vector<int> const >::value << endl; // Yes
cout << is_stl_container_like<array<int,42>>::value << endl; // Yes
cout << is_stl_container_like<polymorphic<vector<int>>>::value << endl; // Yes
cout << is_stl_container_like<function<int(int)>>::value << endl; // No
cout << is_stl_container_like<int>::value << endl; // No
cout << is_stl_container_like<reject<vector<int>>>::value << endl; //No
}

Trevor Hickey
Viele der bereits vorgeschlagenen Lösungen sind ausführlich zum Erkennen von STL-Containern.
Sie konzentrieren sich auf die Eigenschaften, die alle Container besitzen, anstatt explizit anzugeben, was die Container sind.
Wenn Sie eigene Container erstellen und mit einem echten Typ auswerten lassen möchten, empfehle ich die anderen Lösungen. Wenn Sie nur legitime STL-Container und keine STL-ähnlichen Container validieren möchten, ziehen Sie die Verwendung der folgenden Implementierung in Betracht, da sie eine präzise Erkennung von STL-Containern bietet:
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <type_traits>
//specialize a type for all of the STL containers.
namespace is_stl_container_impl{
template <typename T> struct is_stl_container:std::false_type{};
template <typename T, std::size_t N> struct is_stl_container<std::array <T,N>> :std::true_type{};
template <typename... Args> struct is_stl_container<std::vector <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::deque <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::list <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::forward_list <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::set <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::multiset <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::map <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::multimap <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_set <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_multiset<Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_map <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_multimap<Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::stack <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::queue <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::priority_queue <Args...>>:std::true_type{};
}
//type trait to utilize the implementation type traits as well as decay the type
template <typename T> struct is_stl_container {
static constexpr bool const value = is_stl_container_impl::is_stl_container<std::decay_t<T>>::value;
};
Beachten Sie die Verwendung von std::decay
um eine falsche Typableitung basierend auf Typqualifizierern zu vermeiden. Außerdem haben wir Vererbung verwendet std::true_type
und std::false_type
um die Angabe zu vermeiden ::type
tippt uns. Variadische C++11-Vorlagen werden verwendet, um die n Menge an Vorlagentypparametern abzuleiten, die zum Erstellen der Container erforderlich sind.
Die Verwendung der Implementierung ist wie erwartet:
std::cout << std::boolalpha;
std::cout << is_stl_container<std::vector<int>>::value << '\n';
std::cout << is_stl_container<std::vector<int>const&>::value << '\n';
std::cout << is_stl_container<int>::value << '\n';
Drucke:
true
true
false
In C++20 könnten Sie verwenden concept
,
Es liegt an Ihnen, weitere Überprüfungen von dem hinzuzufügen, was Sie als Container betrachten, aber es könnte so aussehen:
template <typename T>
concept Container = requires(T t)
{
std::begin
std::end
};
Anwendungsbeispiel.

kirill_igum
Es gibt is_container
im Schub
http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html
is_container<C>::type
— Ergebnis der Metafunktion, die zu mpl::true_ ausgewertet wird, wenn ein gegebener Typ C als Container behandelt werden soll, ansonsten mpl::false_ Im Allgemeinen muss sich jede Implementierung von is_container so verhalten, als wäre sie ein MPL-Boolean Konstante..

Marc Dirven
Getestet mit MSVC 2019:
template<class C>
struct IsContainer {
private:
template<class D>
static constexpr auto hasValueType() -> decltype(typename D::value_type(), std::true_type()) {
return {};
}
template<class D>
static constexpr auto hasIteratorAlias() -> decltype(typename D::iterator(), std::true_type()) {
return {};
}
template<class D>
static constexpr std::false_type hasIteratorAlias(...) {
return {};
}
template<class D>
static constexpr auto hasConstIteratorAlias() -> decltype(typename D::const_iterator(), std::true_type()) {
return {};
}
template<class D>
static constexpr auto hasBegin() -> decltype(decltype(std::begin(std::declval<D>())){}, std::true_type()) {
return {};
}
template<class D>
static constexpr std::false_type hasBegin(...) {
return {};
}
template<class D>
static constexpr auto hasEnd() -> decltype(decltype(std::end(std::declval<D>())){}, std::true_type()) {
return {};
}
template<class D>
static constexpr std::false_type hasEnd(...) {
return {};
}
template<class D>
static constexpr std::false_type hasConstIteratorAlias(...) {
return {};
}
template<class D>
static constexpr std::false_type hasValueType(...) {
return {};
}
public:
constexpr static bool value = hasValueType<C>().value &&
hasIteratorAlias<C>().value &&
hasConstIteratorAlias<C>().value &&
hasBegin<C>().value &&
hasEnd<C>().value;
constexpr bool operator()() const {
return value;
}
};
Verwendungszweck:
std::vector<int> vec;
int x = 0;
float y = 0.f;
std::array<int, 1> arr{};
constexpr auto val = IsContainer<decltype(vec)>()();
constexpr auto val2 = IsContainer<decltype(x)>()();
constexpr auto val3 = IsContainer<decltype(y)>()();
constexpr auto val4 = IsContainer<decltype(arr)>()();
std::cout << static_cast<bool>(val) << '\n';
std::cout << static_cast<bool>(val2) << '\n';
std::cout << static_cast<bool>(val3) << '\n';
std::cout << static_cast<bool>(val4) << '\n';
Ausgabe:
1
0
0
1
9171800cookie-checkErmitteln Sie zur Kompilierzeit, ob ein Typ ein STL-Container istyes
Ich wäre neugierig zu wissen, warum es genau einer der STL-Container der Standardbibliothek sein muss. Wäre es nicht sinnvoll, die Eigenschaften des Containers zu überprüfen, damit etwas anderes, das diesen Eigenschaften entspricht, problemlos verwendet werden kann?
– Nicol Bolas
23. Februar 2012 um 5:01 Uhr
std::unordered_map
war nicht in der STL, aber jetzt in C++11. Betrachten Sie es als STL-Typ?– MSalter
23. Februar 2012 um 12:00 Uhr
@MSalters: Es tut Containeranforderungen erfüllen.
– Billy ONeal
9. Dezember 2012 um 23:00 Uhr
Wenn Sie entschieden haben, dass ein Container ein STL-Container ist, was werden Sie tun?
– Raseh
1. Mai 2013 um 13:45 Uhr
Bin neugierig, in welchem Szenario / Anwendungsfall würde man eine solche Vorlage verwenden? Weil jeder generische Code, der einen Vektor verwenden würde, keinen assoziativen Container wie map verwenden kann. Wenn der Benutzer Ihrer Vorlage dies nur fragt, damit er dann iterieren kann, existieren die Nicht-Member-STL-Funktionen std::begin() und std::end() (cbegin/cend) hauptsächlich für diesen Zweck
– Chenna V
25. Juni 2017 um 18:30 Uhr