假设我想从一系列非唯一类型中创建一个 * unique * 类型的编译时异构容器,为了做到这一点,我需要遍历源类型(某种tuple),并检查每个类型是否已经存在于我的"unique"元组中。我的问题是:* * 如何检查元组(或boost::fusion容器)是否包含类型?**我对使用STL或boost持开放态度。
tuple
boost::fusion
boost
5sxhfpxr1#
#include <tuple> #include <type_traits> template <typename T, typename Tuple> struct has_type; template <typename T> struct has_type<T, std::tuple<>> : std::false_type {}; template <typename T, typename U, typename... Ts> struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {}; template <typename T, typename... Ts> struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
DEMO如果trait本身应该是 * std::true_type * 或 * std::false_type,还需要一个别名:
std::true_type
std::false_type
template <typename T, typename Tuple> using tuple_contains_type = typename has_type<T, Tuple>::type;
t8e9dugd2#
在C++17中你可以这样做:
template <typename T, typename Tuple> struct has_type; template <typename T, typename... Us> struct has_type<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...> {};
在C11中,你必须滚动你自己的or/disjunction。下面是一个完整的C11版本,带有测试:
or
disjunction
#include <tuple> #include <type_traits> template<typename... Conds> struct or_ : std::false_type {}; template<typename Cond, typename... Conds> struct or_<Cond, Conds...> : std::conditional<Cond::value, std::true_type, or_<Conds...>>::type {}; /* // C++17 version: template<class... B> using or_ = std::disjunction<B...>; */ template <typename T, typename Tuple> struct has_type; template <typename T, typename... Us> struct has_type<T, std::tuple<Us...>> : or_<std::is_same<T, Us>...> {}; // Tests static_assert(has_type<int, std::tuple<>>::value == false, "test"); static_assert(has_type<int, std::tuple<int>>::value == true, "test"); static_assert(has_type<int, std::tuple<float>>::value == false, "test"); static_assert(has_type<int, std::tuple<float, int>>::value == true, "test"); static_assert(has_type<int, std::tuple<int, float>>::value == true, "test"); static_assert(has_type<int, std::tuple<char, float, int>>::value == true, "test"); static_assert(has_type<int, std::tuple<char, float, bool>>::value == false, "test"); static_assert(has_type<const int, std::tuple<int>>::value == false, "test"); // we're using is_same so cv matters static_assert(has_type<int, std::tuple<const int>>::value == false, "test"); // we're using is_same so cv matters
3pmvbmvn3#
因为没有人发布它,我添加一个解决方案的基础上,布尔技巧,我在这里学到的SO:
#include<type_traits> #include<tuple> template<bool...> struct check {}; template<typename U, typename... T> constexpr bool contains(std::tuple<T...>) { return not std::is_same< check<false, std::is_same<U, T>::value...>, check<std::is_same<U, T>::value..., false> >::value; } int main() { static_assert(contains<int>(std::tuple<int, char, double>{}), "!"); static_assert(contains<char>(std::tuple<int, char, double>{}), "!"); static_assert(contains<double>(std::tuple<int, char, double>{}), "!"); static_assert(not contains<float>(std::tuple<int, char, double>{}), "!"); static_assert(not contains<void>(std::tuple<int, char, double>{}), "!"); }
在编译时性能方面,它比accepted solution慢,但值得一提。在C++14中,它甚至更容易编写。标准模板已经在<utility>头文件中提供了所有你需要做的事情:
<utility>
template<typename U, typename... T> constexpr auto contains(std::tuple<T...>) { return not std::is_same< std::integer_sequence<bool, false, std::is_same<U, T>::value...>, std::integer_sequence<bool, std::is_same<U, T>::value..., false> >::value; }
这在概念上与std::get所做的并不远(自C++14起可用于类型),但请注意,如果类型U在T...中出现不止一次,后者将无法编译。是否符合您的要求主要看实际问题。
std::get
U
T...
0md85ypi4#
我的一个项目实际上需要这样的东西。这是我的解决方案:
#include <tuple> #include <type_traits> namespace detail { struct null { }; } template <typename T, typename Tuple> struct tuple_contains; template <typename T, typename... Ts> struct tuple_contains<T, std::tuple<Ts...>> : std::integral_constant< bool, !std::is_same< std::tuple<typename std::conditional<std::is_same<T, Ts>::value, detail::null, Ts>::type...>, std::tuple<Ts...> >::value > { };
这个方法的主要优点是它是一个示例化,不需要递归。
tcbh2hod5#
使用fold expressions的C++17及更高版本解决方案:
template<typename U, typename... T> constexpr bool contains(std::tuple<T...>) { return (std::is_same_v<U, T> || ...); } template<typename U, typename Tuple> constexpr inline bool contains_v = contains<U>(std::declval<Tuple>());
pxyaymoc6#
下面是一个版本,它没有递归地示例化模板来检查匹配的类型,而是使用SFINAE和基于索引的元编程:
#include <type_traits> #include <tuple> template <std::size_t... Indices> struct index_sequence { typedef index_sequence<Indices..., sizeof...(Indices)> next; }; template <std::size_t Start> struct make_index_sequence { typedef typename make_index_sequence<Start - 1>::type::next type; }; template <> struct make_index_sequence<0> { typedef index_sequence<> type; }; template <int n> using make_index_sequence_t = typename make_index_sequence<n>::type; template <typename Value, typename Sequence> struct lookup; template <typename Value, std::size_t... index> struct lookup<Value, index_sequence<index...>> { private: struct null; template <typename... Args> static std::false_type apply(std::conditional_t<std::is_convertible<Args, Value>::value, null, Args>...); template <typename...> static std::true_type apply(...); template <typename... Args> static auto apply_helper(Args&&...) -> decltype(apply<std::remove_reference_t<Args>...>(std::declval<Args>()...)); public: template <typename Tuple> using value = decltype( apply_helper( std::declval< typename std::tuple_element<index, Tuple>::type >()... ) ); }; template <typename Value, typename Tuple> using has_type = decltype( typename lookup<Value, make_index_sequence_t<std::tuple_size<Tuple>::value> >::template value<Tuple>{} );
Live Demo
gzjq41n47#
既然你问了,这里是一个boost::mpl版本:
boost::mpl
#include <boost/mpl/unique.hpp> #include <boost/mpl/sort.hpp> #include <boost/mpl/vector.hpp> #include <boost/type_traits/is_same.hpp> using namespace boost; template<typename Seq> struct unique_concat : mpl::unique<typename mpl::sort<Seq, is_same<mpl::_1,mpl::_2>>::type, is_same<mpl::_1,mpl::_2>> {}; template<typename T> struct print; int main() { typedef mpl::vector<int, float, float, char, int, double, int> input; print<unique_concat<input>::type> asdf; return 0; }
7条答案
按热度按时间5sxhfpxr1#
DEMO
如果trait本身应该是 *
std::true_type
* 或 *std::false_type
,还需要一个别名:t8e9dugd2#
在C++17中你可以这样做:
在C11中,你必须滚动你自己的
or
/disjunction
。下面是一个完整的C11版本,带有测试:3pmvbmvn3#
因为没有人发布它,我添加一个解决方案的基础上,布尔技巧,我在这里学到的SO:
在编译时性能方面,它比accepted solution慢,但值得一提。
在C++14中,它甚至更容易编写。标准模板已经在
<utility>
头文件中提供了所有你需要做的事情:这在概念上与
std::get
所做的并不远(自C++14起可用于类型),但请注意,如果类型U
在T...
中出现不止一次,后者将无法编译。是否符合您的要求主要看实际问题。
0md85ypi4#
我的一个项目实际上需要这样的东西。这是我的解决方案:
这个方法的主要优点是它是一个示例化,不需要递归。
tcbh2hod5#
使用fold expressions的C++17及更高版本解决方案:
pxyaymoc6#
下面是一个版本,它没有递归地示例化模板来检查匹配的类型,而是使用SFINAE和基于索引的元编程:
Live Demo
gzjq41n47#
既然你问了,这里是一个
boost::mpl
版本: