以下是代码片段:
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>
等创建必要的特化。
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>
等创建必要的特化。
注意:以下代码来自于一款名为pretty-print的优秀实用工具,作者是@Kerrek SB(关于它的一个话题在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;
};
首先,您需要定义主模板。默认情况下,该模板将具有一个成员为false的成员:
template <typename T>
struct is_cont {
static const bool value = false;
};
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};
然后,对于您想要检查的类型X,请像这样使用它:
if (is_cont<X>::value) { ... }
#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_type
和std::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
is_stl_container<std::array<Args...>>
看起来不正确。 - Piotr Skotnicki建议使用一种通用的编译时测验方法来测试是否存在类似于STL容器的接口,这个方法定义一个具有STL样式接口的T
类型作为STL样式容器。
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()
方法,或者类似的在编译时探测规范类型接口的方式。
#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
这是一个用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
}
is_container
的函数。这个函数通常用于判断给定类型C是否是一个容器(即可以容纳其他元素的数据结构),如果是,返回mpl::true_,否则返回mpl::false_。需要注意的是,任何实现is_container的代码都应该像MPL布尔常量一样运作。更多详细信息请参考:http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html
已使用 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;
}
};
使用方法:
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';
输出:
1
0
0
1
//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++14解决方案。通过定义enable_if_t
,您可以将其转换为与C++11兼容的代码。
在VS2019和clang 6.0上进行了测试。
对类型T
实现了以下检查:
T::value_type
T::iterator
T::const_iterator
T::size_type
T::begin()
返回类型为T::iterator
的值T::end()
返回类型为T::iterator
的值T::cbegin()
返回类型为T::const_iterator
的值T::cend()
返回类型为T::const_iterator
的值T::size()
返回类型为T::size_type
的值使用void_t
实现了typename检查,如下所示:
template<typename T, typename = void>
constexpr bool has_trait_xxx = false;
template<typename T>
constexpr bool has_trait_xxx<T, void_t<typename T::xxx>> = true;
方法返回类型检查是使用以下方式实现的:void_t
+ enable_if
+ is_same
:
template<typename T, typename = void>
constexpr bool method_x_returns_type_y = false;
template<typename T>
constexpr bool method_x_returns_type_y<T, void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().x()), T::y>>>> = true;
is_stl_container_like_v<T>
的完整代码:
template <typename... T>
using void_t = void;
template <typename T, typename U>
constexpr bool is_same_v = std::is_same<T, U>::value;
#define HAS_XXX_TRAIT_DEF(name) \
template <typename T, typename = void> \
constexpr bool has_##name = false; \
template <typename T> \
constexpr bool has_##name<T, void_t<typename T::name>> = true;
HAS_XXX_TRAIT_DEF(value_type)
HAS_XXX_TRAIT_DEF(iterator)
HAS_XXX_TRAIT_DEF(const_iterator)
HAS_XXX_TRAIT_DEF(size_type)
template <typename T, typename = void>
constexpr bool is_iterable = false;
template <typename T>
constexpr bool
is_iterable<T,
void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().begin()), typename T::iterator>>,
std::enable_if_t<is_same_v<decltype(std::declval<T>().end()), typename T::iterator>>>> = true;
template <typename T, typename = void>
constexpr bool is_const_iterable = false;
template <typename T>
constexpr bool is_const_iterable<
T,
void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().cbegin()), typename T::const_iterator>>,
std::enable_if_t<is_same_v<decltype(std::declval<T>().cend()), typename T::const_iterator>>>> = true;
template <typename T, typename = void>
constexpr bool is_sizeable = false;
template <typename T>
constexpr bool
is_sizeable<T, void_t<std::enable_if_t<is_same_v<decltype(std::declval<T>().size()), typename T::size_type>>>> =
true;
template <typename T>
constexpr bool is_stl_container_like_v = has_value_type<T> && has_iterator<T> && has_const_iterator<T> &&
has_size_type<T> && is_iterable<T> && is_const_iterable<T> && is_sizeable<T>;
使用示例:
std::cout << is_stl_container_like_v<std::vector<int>> << "\n"; // 1
std::cout << is_stl_container_like_v<std::string> << "\n"; // 1
std::cout << is_stl_container_like_v<double> << "\n"; // 0
这种实现存在的问题:
std::vector<int>&
reference
,const_reference
和 difference_type
等类型名称以及 named requirement Container 中提到的许多其他方法。因此,is_stl_container_like_v
不能用于检查自定义类型是否满足 stl 容器的要求。
std::unordered_map
在STL中原本不存在,但现在已经在C++11中加入。您认为它是STL类型吗? - MSaltersSTL 容器
而是一般的可迭代对象
,那么这里有一个答案:https://dev59.com/K2Yr5IYBdhLWcg3wQIC-#53967057 - aniliitb10