c++ 我如何发现一个元组是否包含一个类型?

vpfxa7rd  于 2023-01-10  发布在  其他
关注(0)|答案(7)|浏览(131)

假设我想从一系列非唯一类型中创建一个 * unique * 类型的编译时异构容器,为了做到这一点,我需要遍历源类型(某种tuple),并检查每个类型是否已经存在于我的"unique"元组中。
我的问题是:* * 如何检查元组(或boost::fusion容器)是否包含类型?**
我对使用STL或boost持开放态度。

5sxhfpxr

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,还需要一个别名:

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

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版本,带有测试:

#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
3pmvbmvn

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>头文件中提供了所有你需要做的事情:

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起可用于类型),但请注意,如果类型UT...中出现不止一次,后者将无法编译。
是否符合您的要求主要看实际问题。

0md85ypi

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

这个方法的主要优点是它是一个示例化,不需要递归。

tcbh2hod

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>());
pxyaymoc

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

gzjq41n4

gzjq41n47#

既然你问了,这里是一个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;
}

相关问题