A C++20 draft which seems to be working:
template<typename U, typename... Ts>
constexpr auto get(std::tuple<Ts*...> const& tuple) {
std::array constexpr candidates{std::derived_from<U, Ts>...};
static_assert(std::ranges::count(candidates, true) == 1);
return get<std::ranges::equal_range(candidates, false).size()>(tuple);
}
For some evil reason, this question is tagged as c++11
(which I'm not good at) so I tried to backport my idea. Maybe the following version is suboptimal but I hope that it at least works and this much of constexpr
less horror is enough.
/*
Parameters:
the index of the current tuple type being examined
whether it fits
the derived type
the next remaining tuple type
other remaining tuple types
*/
template<std::size_t, bool, typename...> struct Helper
{ static auto constexpr value = SIZE_MAX; };
template<std::size_t i, typename U, typename T, typename... Ts>
struct Helper<i, false, U, T, Ts...>
: Helper<i + 1, std::is_base_of<T, U>::value, U, Ts...>
{};
template<std::size_t i, typename U, typename... Ts>
struct Helper<i, true, U, Ts...>
{ static auto constexpr value = i; };
template<std::size_t i, typename U, typename T, typename... Ts>
struct Helper<i, true, U, T, Ts...> {
static auto constexpr value = i;
static_assert(
Helper<i + 1, std::is_base_of<T, U>::value, U, Ts...>::value == SIZE_MAX,
"ambiguous"
);
};
template<typename U, typename T, typename... Ts>
auto get(std::tuple<T*, Ts*...> const& tuple)
-> typename std::remove_reference<decltype(std::get<
Helper<0, std::is_base_of<T, U>::value, U, Ts...>::value
>(tuple))>::type {
return std::get<
Helper<0, std::is_base_of<T, U>::value, U, Ts...>::value
>(tuple);
}