在编译时确定类型是否为STL容器

问题描述 投票:18回答:6

我想编写一个模板来确定类型是否是编译时的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>等创建必要的专业化......

c++ templates stl template-meta-programming
6个回答
16
投票

首先,定义主模板,在默认情况下,该模板的成员为false:

template <typename T>
struct is_cont {
  static const bool value = false;
};

然后,您将为容器类型定义部分特化,其值为true:

template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
  static const bool value = true;
};

然后对于要检查的类型X,请使用它

if (is_cont<X>::value) { ... } 

23
投票

注意:下面的代码来自pretty-print编写的一个名为@Kerrek SB的优秀实用程序(在stackoverflow上有一个topic)。

免责声明:我不知道是否允许在未经原作者许可的情况下复制并粘贴此代码。 @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;
};

12
投票

许多已提出的解决方案对于检测STL容器而言都很冗长。 他们专注于所有容器所具有的特性,而不是明确说明容器是什么。

如果您想创建自己的容器并使用真实类型进行评估,我建议使用其他解决方案。如果您只想验证合法的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来避免基于类型限定符的不正确类型推导。此外,我们利用继承std::true_typestd::false_type来避免自己指定::type类型。 C ++ 11可变参数模板用于推导构造容器所需的n量模板类型参数。

正如您所期望的那样使用该实现:

  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

10
投票

建议对has-an-stl-container-like-interface的通用编译时测试是一个合适的解决方案,这个定义了一个类似stl的容器qazxsw poi的接口:

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()

这是一个测试程序,使用GCC 4.7.2,clang 3.2,Intel C ++ 13.1.1构建:

#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

2
投票

在加强#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 } is_container

http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html ---如果给定类型C被视为容器,则求值为mpl :: true_的元函数的结果,mpl :: false_否则通常,is_container的任何实现都需要表现得好像是否为MPL布尔常量..


1
投票

此代码定义容器的特征。它最初来自prettyprint库:

is_container<C>::type

有关更多说明,请参阅//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

相关问题:my blog post

© www.soinside.com 2019 - 2024. All rights reserved.