In the code below, I have worked out a compile time nested loop that calls a print
function that prints the number to screen. The 1D and 2D version work, but the 3D version triggers a note: candidate template ignored: failed template argument deduction
error. What do I do wrong?
#include <functional>
#include <iostream>
// 1D processing.
template<int I>
constexpr void print()
{
std::cout << I << std::endl;
}
template<int... Is>
constexpr void test(
std::integer_sequence<int, Is...> is)
{
(print<Is>(), ...);
}
// End 1D.
// 2D processing.
template<int I, int J>
constexpr void print()
{
std::cout << I << ", " << J << std::endl;
}
template<int I, int... Js>
constexpr void print(
std::integer_sequence<int, Js...> js)
{
(print<I, Js>(), ...);
}
template<int... Is, int... Js>
constexpr void test(
std::integer_sequence<int, Is...> is,
std::integer_sequence<int, Js...> js)
{
(print<Is, Js...>(js), ...);
}
// End 2D.
// 3D processing.
template<int I, int J, int K>
constexpr void print()
{
std::cout << I << ", " << J << ", " << K << std::endl;
}
template<int I, int J, int... Ks>
constexpr void print(
std::integer_sequence<int, Ks...> ks)
{
(print<I, J, Ks>(), ...);
}
template<int I, int... Js, int... Ks>
constexpr void print(
std::integer_sequence<int, Js...> js,
std::integer_sequence<int, Ks...> ks)
{
(print<I, Js, Ks...>(ks), ...);
}
template<int... Is, int... Js, int... Ks>
constexpr void test(
std::integer_sequence<int, Is...> is,
std::integer_sequence<int, Js...> js,
std::integer_sequence<int, Ks...> ks)
{
// THIS CALL FAILS DURING COMPILATION.
(print<Is, Js..., Ks...>(js, ks), ...);
}
// End 3D.
int main()
{
constexpr std::integer_sequence<int, 1, 2, 4, 8> is{};
constexpr std::integer_sequence<int, 1, 2, 4> js{};
constexpr std::integer_sequence<int, 32, 64> ks{};
test(is); // 1D example.
test(is, js); // 2D example.
test(is, js, ks); // 3D example, FAILS.
return 0;
}
The reason for it failing is that the overload of your function print
which takes three template arguments has two variadic template parameter packs template<int I, int... Js, int... Ks>
. Multiple template parameter packs are only valid if the compiler can deduct them from the function arguments (see temp.param/14 ):
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]). A template parameter of a deduction guide template ([temp.deduct.guide]) that does not have a default argument shall be deducible from the parameter-type-list of the deduction guide template.
When explicitly specifying the template parameters the compiler will therefore match them greedily to the first parameter pack . This is even the case when combining type and non-type parameters . Only then deduction occurs from non-template function arguments .
This means in your case
(print<Is, Js..., Ks...>(js, ks), ...);
will actually ignore the wished candidate template
prog.cc:64:16: note: candidate template ignored: failed template argument deduction
as something like
print<I, Js..., Ks...>()
would actually match something like
template <int I, int... Ts>
constexpr void print()
Js...
and Ks...
will be combined to Ts... = {Js..., Ks...}
leaving none of the specified template parameters to the following packs as - by the standard - it must be possible to deduce them from the non-template function arguments.
In order to see why allowing a syntax like in your initial code could be problematic just imagine a template function with two parameter packs Ts
and Ss
, where Ss
can be deducted from the function arguments but Ts
can't be deducted and instead has to specified explicitly,
template <typename... Ts, typename... Ss>
void func(Ss... ss) {
return;
}
which is called with
func<int,double>(int{1}, double{7.0});
This would be ambiguous ( Ts = {int, double}, Ss = {int, double}
or Ts = {}, Ss = {int, double}
) and therefore lead to problems with the logic in your initial code but is unambiguous ( Ts = {int, double}, Ss = {int, double}
) if you match the the parameters greedily and only then deduct from the function arguments.
In your case simply removing the right-most parameter pack
(print<Is, Js...>(js, ks), ...);
or both of them
(print<Is>(js, ks), ...);
makes the compilation succeed as Js
and Ks
can both be deducted from the function arguments js
and ks
. Try it here!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.