如何确定元组是否包含类型
How do I find out if a tuple contains a type?
假设我想从一些非唯一类型序列中创建一个唯一类型的编译时异构容器。为了做到这一点,我需要对源类型(某种tuple
)进行迭代,并检查每个类型是否已经存在于我的"唯一"元组中。
我的问题是:如何检查元组(或boost::fusion
容器)是否包含类型
我对使用STL或boost
持开放态度。
#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 {};
演示
以及一个额外的别名,如果特征本身应该是std::true_type
或std::false_type
:
template <typename T, typename Tuple>
using tuple_contains_type = typename has_type<T, Tuple>::type;
在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>...> {};
在C++11中,您必须滚动自己的or
/disjunction
。这是一个完整的C++11版本,带有测试:
#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
因为没有人发布它,我在SO:上学到的bool技巧的基础上又添加了一个解决方案
#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>{}), "!");
}
就编译时性能而言,它比公认的解决方案慢,但值得一提
在C++14中,编写起来会更容易。标准模板已经提供了<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...
中出现多次,则后者将无法编译
它是否符合您的要求主要取决于实际问题。
对于一个项目,我实际上需要这样的东西。这就是我的解决方案:
#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
>
{ };
这种方法的主要优点是它是一个实例化,不需要递归。
C++17和使用折叠表达式的up解决方案:
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>());
这里有一个版本,它不会递归实例化模板来检查匹配的类型。相反,它使用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>{}
);
实时演示
既然你要求它,这里有一个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;
}
相关文章:
- 包含的类类型显然没有声明,为什么?
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- C++(和 ROS) - 包含与前向声明引用,设置默认值和类型定义
- C2011: 'Card':"类"类型重新定义(尽管使用了包含保护并且没有在文件中重新定义.cpp类)
- 包含它的模板化类型的shared_ptr
- C++如何乘以包含 std::variant 元素的向量的迭代器?正在执行迭代器类型的转换?
- C++包含函数标头会给出错误'__dest'未命名类型
- 何时包含内置类型和运算符的标头?
- 生成包含给定类型的 N 个参数的可变参数列表的最佳方法?
- 指针数据类型变量如何包含对象?
- 如何检查联合是否包含类型(使用 type_traits)?
- 包含类型的类型的模板函数重载
- 是否可以推断出 std::insert_iterator 的包含类型
- 是否允许unique_prts隐式转换其包含类型
- LNK2005已定义在项目 [MSVC12] 中包含 C 类型头文件 C++错误
- 具有不同包含类型的 STL 容器
- 如何确定元组是否包含类型
- 如何创建一个包含类型尚不知道的值的类?
- 在声明包含类型的对象时遇到问题
- C++读取二进制文件,其中包含类型为double的数字