I'm trying to find type A
in a parameter pack:
template <int I, typename A, typename B, typename ...C>
struct index_of
{
static constexpr int const value =
std::is_same<A, B>::value ? I : index_of<I + 1, A, C...>::value;
};
template <int I, typename A, typename B>
struct index_of<I, A, B>
{
static constexpr int const value =
std::is_same<A, B>::value ? I : -1;
};
This seems to work, but I am unable to eliminate the nontype parameter I
, which I would like to be a default parameter, but can't make it such, because of the parameter pack at the end. How to eliminate/hide I
, so the metafunction becomes more user-friendly?
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration.
Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.
Pack expansion A pattern followed by an ellipsis, in which the name of at least one parameter pack appears at least once, is expanded into zero or more comma-separated instantiations of the pattern, where the name of the parameter pack is replaced by each of the elements from the pack, in order. template<class...
You can hide this implementation in a namespace and use another class that calls your implementation with a default parameter example:
namespace detail
{
// your code as it is in the question
}
template <typename A, typename... B>
struct index_of
{
static int const value = detail::index_of<0, A, B...>::value;
};
Edit
In his comment DyP suggests a simpler way to default I
using an alias
template <typename A, typename... B>
using index_of = detail::index_of<0, A, B...>;
template <typename A, typename B, typename... C>
struct index_of
{
static constexpr int const value =
std::is_same<A, B>{}
? 0
: (index_of<A, C...>::value >= 0) ? 1+index_of<A, C...>::value : -1;
};
template <typename A, typename B>
struct index_of<A, B>
{
static constexpr int const value = std::is_same<A, B>{} -1;
};
Note the std::is_same<A, B>{} -1
uses a conversion from bool
to int
.
Better by deriving from integral_constant
:
template <typename A, typename B, typename... C>
struct index_of
: std::integral_constant
< int,
std::is_same<A, B>{}
? 0
: (index_of<A, C...>{} == -1 ? -1 : 1+index_of<A, C...>{})
>
{};
template <typename A, typename B>
struct index_of<A, B>
: std::integral_constant < int, std::is_same<A, B>{} -1 >
{};
If you don't need to return -1
in case the type isn't found:
(if anyone knows how to incorporate a static_assert
here for a pretty diagnostic message, I'd appreciate a comment/edit)
template <typename A, typename B, typename... C>
struct index_of
: std::integral_constant < std::size_t,
std::is_same<A, B>{} ? 0 : 1+index_of<A, C...>{} >
{};
template <typename A, typename B>
struct index_of<A, B>
: std::integral_constant<std::size_t, 0>
{
constexpr operator std::size_t() const
{
return std::is_same<A, B>{}
? 0
: throw std::invalid_argument("Type not found!");
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With