如何确定元组是否包含类型?

qua*_*ant 33 c++ c++11

假设我想从一些非唯一类型的序列中创建一个独特类型的编译时异构容器.为了做到这一点,我需要迭代源类型(某种类型tuple)并检查每个类型是否已存在于我的"唯一"元组中.

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

我愿意使用STL或boost.

Pio*_*cki 30

#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 {};
Run Code Online (Sandbox Code Playgroud)

DEMO

另外一个别名,如果特征本身应该 std::true_type std::false_type:

template <typename T, typename Tuple>
using tuple_contains_type = typename has_type<T, Tuple>::type;
Run Code Online (Sandbox Code Playgroud)


Ros*_*ina 14

在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>...> {};
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)


Ben*_*aub 9

使用折叠表达式的C++17 及更高版本解决方案:

template<typename U, typename... T>
constexpr bool contains(std::tuple<T...>) {
    return (std::is_same_v<U, T> || ...);
}

assert( contains<int   >(std::declval<std::tuple<int, float>>()));
assert( contains<float >(std::declval<std::tuple<int, float>>()));
assert(!contains<double>(std::declval<std::tuple<int, float>>()));

//// In case std::declval ever becomes constexpr, the following should work as well:
// template<typename U, typename Tuple>
// constexpr inline bool contains_v = contains<U>(std::declval<Tuple>());
Run Code Online (Sandbox Code Playgroud)


cda*_*ara 8

我实际上需要这样的项目.这是我的解决方案:

#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
  >
{ };
Run Code Online (Sandbox Code Playgroud)

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


sky*_*ack 6

由于没有人发布,因此我将根据我在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>{}), "!");
}
Run Code Online (Sandbox Code Playgroud)

就编译时性能而言,它比公认的解决方案要慢,但是值得一提。


在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;
}
Run Code Online (Sandbox Code Playgroud)

从概念上讲,这与std::get所做的事情相差不远(从C ++ 14开始,类型可用),但是请注意,如果类型U在中多次出现,后者将无法编译T...
是否符合您的要求主要取决于实际问题。

  • 在我看来,这是迄今为止最干净(最易读)的 C++14 解决方案。只有在下面 @Benno Straub 的 C++17 答案中使用折叠表达式才比它黯然失色。 (2认同)