print a list using variadic templates

Viewed 95

I need your help to find out why does following code not compile.

#include <iostream>

template <int I>
void foo(){
  std::cout << I << std::endl;
  std::cout << "end of list" << std::endl;
}

template <int I, int ... Ints>
void foo(){
  std::cout << I << std::endl;
  foo<Ints...>();
}


int main(){
  foo<1, 2>();
  return 0;
}

I am getting this error.

function_parameter_pack.cpp: In instantiation of ‘void foo() [with int I = 1; int ...Ints = {2}]’:
function_parameter_pack.cpp:17:13:   required from here
function_parameter_pack.cpp:12:15: error: call of overloaded ‘foo<2>()’ is ambiguous
   foo<Ints...>();
   ~~~~~~~~~~~~^~
function_parameter_pack.cpp:4:6: note: candidate: void foo() [with int I = 2]
 void foo(){
      ^~~
function_parameter_pack.cpp:10:6: note: candidate: void foo() [with int I = 2; int ...Ints = {}]
 void foo(){

Isn't a more specialized function is supposed to be chosen? i.e one with only one template(the first one).

3 Answers

foo<2> matches both templates equally well (since int... matches zero ints too) which is why the compiler complains about ambiguity.

Isn't a more specialized function is supposed to be chosen?

Yes, but a these are equally special. Deducing which is the more specialized matching function is done by looking at the arguments to the function, not the template parameters.

One way to solve that is to make the second function template take 2 or more template parameters:

#include <iostream>

template<int I>
void foo() {
    std::cout << I << std::endl;
    std::cout << "end of list" << std::endl;
}

template<int I, int J, int... Ints>
void foo() {
    std::cout << I << std::endl;
    foo<J, Ints...>();
}

int main() {
    foo<1>();
    foo<1, 2>();
    foo<1, 2, 3>();
}

Another way to solve the problem is using a do-nothing terminal recursion function accepting something different from a value (maybe a typename), but with a default,

template <typename = void>
void foo() {
  std::cout << "end of list" << std::endl;
}

that intercept a foo<Ints...>() call when Ints... is empty.

The recursive case, that intercept every template integer value, remain the same

template <int I, int ... Ints>
void foo(){
  std::cout << I << std::endl;
  foo<Ints...>();
}

I prefer solution which uses dummy array, so it works similarly to fold expression.

template<int... Ints>
void foo() {
    char dummy[] {((std::cout << Ints << ','), '0')...};
    std::cout << '\n';
}

https://godbolt.org/z/1Y1noK

Related