Skip to content

Instantly share code, notes, and snippets.

@dgodfrey206
Last active August 29, 2015 14:06
Show Gist options
  • Save dgodfrey206/c194bff6494faad66e34 to your computer and use it in GitHub Desktop.
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.
#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