1

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;
}
3
  • I'm unfortunately not sure why exactly this fixes it, but in the 3D test() function, omit the second and third template parameters to the call to print(): (print<Is>(js, ks), ...); Commented Jun 20, 2021 at 8:05
  • @G.Sliepen. Indeed, this fixes it. It would be great to learn why. Commented Jun 20, 2021 at 8:07
  • My guess is that in print<Is, Js..., Ks...>(), the boundary between Js... and Ks... gets lost, but if you let it autodeduce based on the arguments, it does know where the boundary is. Commented Jun 20, 2021 at 8:31

1 Answer 1

1

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!

Sign up to request clarification or add additional context in comments.

5 Comments

Alright, so imagine I would like to write out the three template arguments for clarity, how should I do this?
@Chiel I don't think there are ways of doing so. If you want to be more precise about which function will be called you could rename the different print functions to have unique names.
It is clear to me which function is called. I would only like to know what is happening under the hood if I omit the template argument, because my actual use case is a bit more complex. Your answer solves my problem, but I do not really understand it fully.
@Chiel I see, I modified my answer. Maybe it is more clear now.
@Chiel Off-topic: Had a look at your microhh code. Really awesome stuff! I am a big fan of DNS/LES simulations, fluid flow instabilities and atmospheric flows. What are you using for visualistion? Only Python and in combination with matplotlib?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.