2014-09-21 53 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 
相關問題