【问题标题】:How do I find out if a tuple contains a type?如何确定元组是否包含类型?
【发布时间】:2014-11-15 11:38:49
【问题描述】:

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

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

我愿意使用 STL 或 boost

【问题讨论】:

  • 哪个版本的 C++?
  • @Deduplicator 如果未指定,我们将假定为 C++11。
  • 如果您已经在使用boost,为什么不使用 MPL?这将是一项微不足道的任务。
  • @Deduplicator 是的,我正在使用 C++11。我假设c++ 标签只是指当前版本?
  • @pmr,如果这可以通过“微不足道的任务”来解决,我会全力以赴!

标签: c++ c++11


【解决方案1】:
#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_type std::false_type

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

【讨论】:

  • 我不明白它是如何工作的......你介意解释一下吗?
  • 这很聪明!我想我从来没有想过这个。谢谢:)
  • @GingerPlusPlus:它递归地剥离元组的头部类型(只留下它的尾部),直到新头部与T相同(然后它最终继承自true_type停止递归),或者元组没有更多类型(然后它最终继承自false_type)。请注意,&lt;T, tuple&lt;T, ...&gt;&gt;T 与 head T 相同)和 &lt;T, tuple&lt;U, ...&gt;&gt;T 与 head U 不同)有特殊情况。这与 tuple 自身的定义方式非常相似。
  • @GingerPlusPlus:即tuple&lt;T, Ts...&gt;私下继承自tuple&lt;Ts...&gt;
  • 很好的答案,但你的常量应该是一个布尔值。在 c++17 中:template &lt;class T, class Tuple&gt;inline constexpr bool tuple_contains_v = has_type&lt;T, Tuple&gt;::value;
【解决方案2】:

在 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

【讨论】:

  • 对于pythonists,不是or_,而是any
【解决方案3】:

因为没有人发布它,所以我正在添加一个基于我在这里学到的 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>{}), "!");
}

就编译时性能而言,它比accepted solution 慢,但值得一提。


在 C++14 中,它会更容易编写。标准模板已经在 &lt;utility&gt; 标头中提供了您需要执行的所有操作:

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... 中多次出现,则后者无法编译。 是否符合您的要求主要取决于实际问题。

【讨论】:

  • 这是迄今为止 IMO 最干净(最易读)的 C++14 解决方案。只有在下面的@Benno Straub 的 C++17 答案中使用折叠表达式才能胜过它。
【解决方案4】:

我实际上需要这样的东西来做一个项目。这是我的解决方案:

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

这种方法的主要优点是它是一次实例化,不需要递归。

【讨论】:

  • 当我尝试使用它时,我无法编译它。 Clang 抱怨 tuple_contains 已声明但未定义。您介意提供示例调用代码吗?
  • std::cout>::value
【解决方案5】:

使用fold expressions 的C++17 及更高版本解决方案:

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

【讨论】:

    【解决方案6】:

    这是一个不递归实例化模板来检查匹配类型的版本。相反,它使用 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】:

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

      【讨论】:

      • 使用g++ -Wall -Wextra -pedantic -Wno-sign-compare -Wno-long-long -o tpl_b tuple_boost.cpp 构建会产生错误:tuple_boost.cpp:19:37: error: aggregate ‘print&lt;boost::mpl::v_item&lt;double, boost::mpl::v_item&lt;char, boost::mpl::v_item&lt;float, boost::mpl::v_item&lt;int, boost::mpl::vector0&lt;mpl_::na&gt;, 0&gt;, 0&gt;, 0&gt;, 0&gt; &gt; asdf’ has incomplete type and cannot be defined 具体错误:print&lt;unique_concat&lt;input&gt;::type&gt; asdf;
      • 抱歉,版本信息是gcc 4.9.1-1boost 1.55.0-6
      • @DavidC.Rankin 这是有意的。这是查看类型名称的简单方法。
      • 为什么排序+唯一?已经有mpl::has_key。这不是 OP 想要的吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-25
      • 2013-10-04
      • 2017-04-01
      • 2010-11-10
      相关资源
      最近更新 更多