Last active
August 29, 2015 14:06
-
-
Save dgodfrey206/c194bff6494faad66e34 to your computer and use it in GitHub Desktop.
Checks if there is a type within a tuple without repeatedly instantiating the template to check each type.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <tuple> | |
// Sequence of integers to index the elements of the 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; | |
}; | |
// To prevent having to type "typename" excessively | |
template <int n> | |
using make_index_sequence_t = typename make_index_sequence<n>::type; | |
// no_decay is a class template that allows the following machinery to work with incomplete types by wrapping | |
// them in a type name | |
template <typename> | |
struct no_decay { }; | |
// Value: The type to search for | |
// Sequence: The sequence of integers from [0, std::tuple_size<Tuple>::value) that may or may not index Value | |
template <typename Value, typename Sequence> | |
struct lookup; | |
template <typename Value, std::size_t... index> | |
struct lookup<Value, index_sequence<index...>> | |
{ | |
static_assert(sizeof...(index) > 0, "Tuple is empty."); | |
private: | |
// This is a sentinel type that is_convertible will return when the below argument pack contains | |
// Value. When that happens substitution will fail and the fallback apply() method will be chosen | |
struct null; | |
// The pack expansion allows us to check every argument in Args... and test its convertibility. If overload resolution | |
// falls back to the second overload of apply(), that means we have found a match and the return type will be | |
// true_type, otherwise false_type | |
template <typename... Args> | |
static std::false_type | |
apply(std::conditional_t<std::is_convertible<Args, no_decay<Value>>::value, null, Args>...); | |
// Fallback | |
template <typename...> | |
static std::true_type apply(...); | |
// The first overload of apply() prevents template argument deduction. Because of this we would have to | |
// explicitly specify the template arguments which can bloat the code and make it messy, especially since we | |
// would have to also provide the arguments to the function as well. apply_helper() is simply a forwarder | |
// that deduces the types of the parameters and passes them off to the primary apply() template | |
template <typename... Args> | |
static auto apply_helper(Args&&...) -> | |
decltype(apply<std::remove_reference_t<Args>...>(std::declval<Args>()...)); | |
public: | |
// Call apply_helper with each tuple element type | |
template <typename Tuple> | |
using value = decltype( | |
apply_helper( | |
std::declval<no_decay< | |
typename std::tuple_element<index, Tuple>::type | |
>>()... | |
) | |
); | |
}; | |
// Call lookup for the type and generate the sequence; value<> is either true_type or false_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>{} | |
); | |
// Test: | |
class A; | |
class B; | |
class C; | |
typedef std::tuple<A, B, A> t1; | |
typedef std::tuple<B, C, C> t2; | |
// This will not fire: | |
static_assert(has_type<B, t1>{}, ""); | |
// This WILL fire | |
static_assert(has_type<A, t2>{}, ""); | |
int main() | |
{ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment