可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Suppose I want to create a compile-time heterogenous container of unique types from some sequence of non-unique types. In order to do this I need to iterate over the source type (some kind of tuple
) and check whether each type already exists in my "unique" tuple.
My question is: How can I check whether a tuple (or a boost::fusion
container) contains a type?
I'm open to using either the STL or boost
.
回答1:
#include #include template struct has_type; template struct has_type> : std::false_type {}; template struct has_type> : has_type> {}; template struct has_type> : std::true_type {};
DEMO
And an additional alias, if the trait itself should be std::true_type
or std::false_type
:
template using tuple_contains_type = typename has_type::type;
回答2:
I actually needed something like this for a project. This was my solution:
#include #include namespace detail { struct null { }; } template struct tuple_contains; template struct tuple_contains> : std::integral_constant::value, detail::null, Ts>::type...>, std::tuple >::value > { };
The main advantage of this method is that it's one instantiation, no recursion required.
回答3:
Because nobody posted it, I'm adding one more solution based on the bool trick I've learned about here on SO:
#include #include template struct check {}; template constexpr bool contains(std::tuple) { return not std::is_same::value...>, check<:is_same t="">::value..., false> >::value; } int main() { static_assert(contains(std::tuple{}), "!"); static_assert(contains(std::tuple{}), "!"); static_assert(contains(std::tuple{}), "!"); static_assert(not contains(std::tuple{}), "!"); static_assert(not contains(std::tuple{}), "!"); }
In terms of compile-time performance it's slower than the accepted solution, but it's worth to mention it.
In C++14 it would be even easier to write. The standard template offers already all what you need to do that in the
header:
template constexpr auto contains(std::tuple) { return not std::is_same::value...>, std::integer_sequence::value..., false> >::value; }
This is not far conceptually from what std::get
does (available since C++14 for types), but note that the latter fails to compile if the type U
is present more than once in T...
.
If it fits with your requirements mostly depends on the actual problem.
回答4:
Here is a version that does not recursively instantiate the template to check for a matching type. Instead it uses SFINAE with indices-based meta-programming:
#include #include template <:size_t... indices=""> struct index_sequence { typedef index_sequence next; }; template <:size_t start=""> struct make_index_sequence { typedef typename make_index_sequence::type::next type; }; template struct make_index_sequence { typedef index_sequence type; }; template using make_index_sequence_t = typename make_index_sequence::type; template struct lookup; template struct lookup> { private: struct null; template static std::false_type apply(std::conditional_t<:is_convertible value="">::value, null, Args>...); template static std::true_type apply(...); template static auto apply_helper(Args&&...) -> decltype(apply<:remove_reference_t>...>(std::declval()...)); public: template using value = decltype( apply_helper( std::declval::type >()... ) ); }; template using has_type = decltype( typename lookup::value> >::template value{} );
Live Demo
回答5:
In C++17 you can do it like this:
template struct has_type; template struct has_type> : std::disjunction<:is_same us="">...> {};
In C++11 you have to roll your own or
/ disjunction
. Here's a full C++11 version, with tests:
#include #include template struct or_ : std::false_type {}; template struct or_ : std::conditional<:value std::true_type="" or_="">>::type {}; /* // C++17 version: template using or_ = std::disjunction; */ template struct has_type; template struct has_type> : or_<:is_same us="">...> {}; // Tests static_assert(has_type>::value == false, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == false, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == false, "test"); static_assert(has_type>::value == false, "test"); // we're using is_same so cv matters static_assert(has_type>::value == false, "test"); // we're using is_same so cv matters
回答6:
Since you asked for it, here is a boost::mpl
version:
#include #include #include #include using namespace boost; template struct unique_concat : mpl::unique>::type, is_same<:_1>> {}; template struct print; int main() { typedef mpl::vector input; print::type> asdf; return 0; }