Strange overload resolution with variational function templates

I have the following code:

#include <iostream>

template <typename... Args>
void f(int a, int b, Args... args) { 
    std::cout << b << '\n';
    f(a, args...);
}
void f(int, int b) {
    std::cout << b << '\n';     
}

int main() {
  f(1, 2);
  //f(1, 2, 3);
}

      

While f(1, 2)

compiling, f(1, 2, 3)

no. From the error message generated by the compiler, I can see what f<>

is being generated somehow. Inside the instance, a call is made f(a)

and hence an error. What causes the compiler to not use f(int, int)

but try to instantiate f<>(int, int)

while parsing the call f(1, 2, 3)

?

+3


source to share


1 answer


In the variational function template f()

f

in the recursive call is the dependent name due to [temp.dep], the main focus is:

In an expression, the form:

postfix-expression ( expression-listopt)

      

where postfix expression is unqualified-id, unqualified-id denotes a dependent name if (1.1) is any of the expressions in the expression list a package extension (14.5.3),

And according to [temp.dep.res] my blow is:

When resolving dependent names, names from the following sources are considered:
(1.1) - Declarations that are visible at the point of the template definition .
(1.2) - Declarations from namespaces associated with the types of function arguments both from the context of the context (14.6.4.1) and the context of the definition.

There is only one declaration f

that is visible at the definition point template <typename... Args> void f(int, int, Args...)

, and that is itself. The second point doesn't apply here because all of your arguments are there int

, and there are no associated namespaces for the underlying types. Since this function template cannot be called with a single argument, you get a compilation error.



The solution is to restructure your code so that your base case is visible at the point of definition, that is:

// this can be just the declaration
void f(int, int ) { /* ... */ } 

template <typename... Args>
void f(int a, int b, Args... args) 
{ 
    std::cout << b << '\n';
    f(a, args...); // now this will call f(int, int) 
                   // if sizeof...(Args) == 1
}

      

An example that applies (1.2) would be as follows:

#include <iostream>

template <typename A, typename... Args>
void f(A a, int b, Args... args) { 
    std::cout << b << '\n';
    f(a, args...);
}

template <typename A>
void f(A a, int b) {
    std::cout << b << '\n';     
}

struct bar {};

int main() {
    //f(1,2,3);     // still doesn't compile, same reasoning
    f(bar{}, 2, 3); // OK. bar is in the global namespace, so declarations
                    // from the global namespace in both instantiation 
                    // and definition context are considered, which includes
                    // the second `f`.
}

      

+3


source







All Articles