I have a template function in C++17, where the N first arguments need to be integral type, and this should be checked during template substitution. (The template function should not exists for arguments that don't meet the condition, so static_assert()
in the function body does not work.)
It is a constructor of a class, so SFINAE is done in a template argument.
For fixed N = 2, this works:
struct A {
template<class Arg1, class Arg2, class... OtherArgs,
std::enable_if_t<(std::is_integral_v<Arg1> && std::is_integral_v<Arg2>), int> = 0
>
A(Arg1 arg1, Arg2 arg2, OtherArgs... otherArgs)
};
However when N
is a template argument, there seems to be no obvious way.
To check if all arguments are integrals, this works:
template<std::size_t N>
struct A {
template<class... Args,
std::enable_if_t<std::conjunction_v<std::is_integral<Args>...>, int> = 0
>
A(Args... args)
};
Is there a tool like INDEX_OF
here, which would get the index of an argument in the parameter pack, so that is_integral
only needs to be true for the first N arguments?
template<std::size_t N>
struct A {
template<class... Args,
std::enable_if_t<std::conjunction_v<
std::disjunction<
std::bool_constant< (INDEX_OF(Args) >= N) >,
std::is_integral<Args>
>...
>, int> = 0
>
A(Args... args)
};
You might create constexpr function:
template <std::size_t N, typename... Ts>
static constexpr bool first_are_integral()
{
constexpr std::array<bool, sizeof...(Ts)> bools{std::is_integral_v<Ts>...};
for (std::size_t i = 0; i != N; ++i) {
if (!bools[i]) {
return false;
}
}
return true;
// C++20
// return std::all_of(std::begin(bools), std::begin(bools) + N, [](bool b){ return b; });
}
And then
template <class... Args,
std::enable_if_t<first_are_integral<N, Args...>(), int> = 0
>
A(Args...){ /*...*/ }
Alternatively, std::index_sequence
is a way to have index and type (from tuple)
template <typename Seq, typename Tuple>
struct first_are_integral_impl;
template <std::size_t...Is, typename Tuple>
struct first_are_integral_impl<std::index_sequence<Is...>, Tuple>
{
constexpr static bool value =
(std::is_integral_v<std::tuple_element_t<Is, Tuple>> && ...);
};
template <std::size_t N, typename Tuple>
constexpr bool first_are_integral_v =
first_are_integral_impl<std::make_index_sequence<N>, Tuple>::value;