2014-09-21 45 views
21

假设我想从一些非唯一类型的序列创建一个编译时异构容器的唯一类型。为了做到这一点,我需要迭代源类型(某种tuple)并检查每个类型是否已经存在于我的“唯一”元组中。如何找出元组是否包含类型?

我的问题是:如何检查一个元组(或一个boost::fusion容器)是否包含一个类型?

我可以使用STL或boost

+1

哪个版本的C++? – Deduplicator 2014-09-21 10:30:10

+0

@Deduplicator如果没有指定,我们将假定C++ 11。 – Shoe 2014-09-21 10:30:44

+0

如果你已经使用'boost'为什么不使用MPL?这将是一件微不足道的任务。 – pmr 2014-09-21 10:31:33

回答

26
#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

和一个额外的别名,如果性状本身应该std::true_typestd::false_type

template <typename T, typename Tuple> 
using tuple_contains_type = typename has_type<T, Tuple>::type; 
+0

我不明白它是如何工作的...你介意解释一下吗? – GingerPlusPlus 2014-09-21 11:14:58

+0

这非常聪明!我不认为我会想到这一点。谢谢:) – arman 2014-09-21 11:15:36

+0

@GingerPlusPlus:它递归地去掉元组头部类型(只留下它的尾部),直到新头部与'T'相同(然后它最终从'true_type'继承了递归),或者元组没有更多的类型(那么它最终会从'false_type'继承)。注意''T,元组>'('T'与头'T'相同并且' 2014-09-21 11:38:19

1

既然你自找的,在这里是一个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; 
} 
+0

'g ++ -Wall -Wextra -pedantic -Wno-sign-compare -Wno-long-long -o tpl_b tuple_boost.cpp'构建产生一个错误:'tuple_boost.cpp:19:37:error:aggregate'print ,0>,0>,0>,0 >> asdf'具有不完整的类型并且不能被定义'具体错误:'print :: type > asdf;' – 2014-09-24 01:26:24

+0

我很抱歉,版本信息是'gcc 4.9.1-1'和'boost 1.55.0-6'。 – 2014-09-24 01:42:10

+0

@ DavidC.Rankin这是打算。这是查看类型名称的简单方法。 – pmr 2014-09-24 08:57:48

3

这里是不递归实例化模板,以检查是否有匹配型版本。相反,它使用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

7

我真正需要的是这样的一个项目。这是我的解决方案:

#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 
    > 
{ }; 

这种方法的主要优点是,它的一个实例,无需递归。

+0

当我尝试使用它时,我无法将其编译。铿锵声称tuple_contains已声明但尚未定义。你介意提供示例调用代码吗? – 2016-09-12 19:45:27

+1

std :: cout << std :: boolalpha << tuple_contains > :: value <<'\ n'; – cdacamara 2016-10-11 14:51:50

4

因为没有人贴吧,我加入基础上,布尔招多了一个解决方案,我已经了解这里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>头什么:

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...中出现不止一次,则进行编译。
如果符合您的要求主要取决于实际问题。

3

在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 
相关问题