在编译时确定types是否是STL容器
我想编写一个模板来确定一个types在编译时是否是一个stl容器。
我有以下一些代码:
struct is_cont{}; struct not_cont{}; template <typename T> struct is_cont { typedef not_cont result_t; };
但我不知道如何为std::vector<T,Alloc>, deque<T,Alloc>, set<T,Alloc,Comp>
等创build必要的特化。
首先,你定义你的主模板,在默认情况下,它的成员是false。
template <typename T> struct is_cont { static const bool value = false; };
然后,您将为具有true值的容器types定义部分特化:
template <typename T,typename Alloc> struct is_cont<std::vector<T,Alloc> > { static const bool value = true; };
然后,对于要检查的Xtypes,请使用它
if (is_cont<X>::value) { ... }
注意:下面的代码是从@Kerrek SB写的一个名为pretty-print的优秀实用程序中获得的 (在stackoverflow上它是一个主题 )。
免责声明:我不知道我是否被允许在这里复制和粘贴这个代码,而没有得到原作者的许可。 @Kerrek,让我知道如果你有任何问题。 🙂
你可以使用这个类模板:
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> { };
用法:
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
请注意, is_container
需要以下辅助类模板:
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; };
追求对has-an-stl-container-like-interface进行通用编译时testing的build议是一个合适的解决scheme,这个接口定义了一个类似stl的容器 T
:
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 &
额外的需求,例如size()
方法,可以以明显的方式添加,或者在编译时以明显类似的方式探测其他规范types的接口。
#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
这里是一个testing程序,使用GCC 4.7.2,clang 3.2,Intel C ++ 13.1.1构build:
#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 }
许多已经提出的解决scheme对于检测STL容器是冗长的。
他们专注于所有容器所具有的特性,而不是明确说明容器是什么。
如果你想创build自己的容器,并用真正的types进行评估,我会推荐其他的解决scheme。 如果您只想validation合法的STL容器而不是类似STL的容器,请考虑使用以下实现,因为它提供了精确的STL容器检测:
#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; };
请注意使用std::decay
来避免基于types限定符的错误types推导。 另外,我们使用了inheritancestd::true_type
和std::false_type
来避免自己指定::type
types。 C ++ 11可变参数模板用于推导构build容器所需的n个模板types参数。
使用实现是你所期望的:
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';
打印:
true true false
boost中有is_container
http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html
is_container<C>::type
—如果一个给定typesC被视为一个容器,则计算为mpl :: true_的元函数的结果,否则mpl :: false_一般来说,任何is_container实现都需要performance就好像是一个MPL布尔常量..
这段代码定义了容器的特征。 它最初来自相纸库:
//put this in type_utils.hpp #ifndef commn_utils_type_utils_hpp #define commn_utils_type_utils_hpp #include <type_traits> #include <valarray> namespace common_utils { namespace type_utils { //from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp //also see https://gist.github.com/louisdx/1076849 namespace detail { // SFINAE type trait to detect whether T::const_iterator exists. struct sfinae_base { using yes = char; using no = yes[2]; }; template <typename T> struct has_const_iterator : private sfinae_base { private: template <typename C> static yes & test(typename C::const_iterator*); template <typename C> static no & test(...); public: static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes); using type = T; void dummy(); //for GCC to supress -Wctor-dtor-privacy }; template <typename T> struct has_begin_end : private sfinae_base { private: template <typename C> static yes & 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>::type *); template <typename C> static no & f(...); template <typename C> static yes & 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*); template <typename C> static no & g(...); public: static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes); static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes); void dummy(); //for GCC to supress -Wctor-dtor-privacy }; } // namespace detail // Basic is_container template; specialize to derive from std::true_type for all desired container types template <typename T> struct is_container : public std::integral_constant<bool, detail::has_const_iterator<T>::value && detail::has_begin_end<T>::beg_value && detail::has_begin_end<T>::end_value> { }; template <typename T, std::size_t N> struct is_container<T[N]> : std::true_type { }; template <std::size_t N> struct is_container<char[N]> : std::false_type { }; template <typename T> struct is_container<std::valarray<T>> : std::true_type { }; template <typename T1, typename T2> struct is_container<std::pair<T1, T2>> : std::true_type { }; template <typename ...Args> struct is_container<std::tuple<Args...>> : std::true_type { }; }} //namespace #endif
更多的解释请参阅我的博客文章 。
相关问题: c ++模板类; 函数与任意容器types,如何定义它?