3

I'm using variadic function templates in the common recursive format and I need to change the behaviour of the function whenever I'm handling a vector. If the functions templates were not variadic, overloading works well, but with variadic function templates, the overloading resolution seems to change when unpacking the argument pack.

Below some code to explain better what I mean.

#include <iostream>
#include <vector>

template<typename T>
void complexfun(T x) {
    std::cout << "1 end" << std::endl;
}

template<typename T, typename... Args>
void complexfun(T x, Args... args) {
    std::cout << "1 ";
    complexfun(args...);
}

template<typename T>
void complexfun(std::vector<T> x) {
    std::cout << "2 end" << std::endl;
}

template<typename T, typename... Args>
void complexfun(std::vector<T> x, Args... args) {
    std::cout << "2 ";
    complexfun(args...);
}

int main() {
    std::vector<int> vint = {2, 3, 4};
    float x1 = 9.4;
    
    complexfun(vint); // output: 2 end -> OK
    complexfun(vint, x1); // output: 2 1 end -> OK
    complexfun(x1, vint); // output: 1 1 end -> WRONG: need 1 2 end
    
    return 0;
}

In the execution of complexfun(x1, vint) we should have complexfun(vint), but it does not behave as the "standalone" call complexfun(vint).

Any help on why this is the case and how to fix it is greatly appreciated!

Ivo
  • 33
  • 4
  • 1
    The compiler goes from overload 2 to oberload 1 because at 2 it does not see oberload 3. At 2 it will consider only 1 and 2 (itself) and functions found by ADL. 3 and 4 are not among those. This does not really have anything to do with vararg templates. – Johannes Schaub - litb Mar 31 '22 at 15:45

1 Answers1

2

You need to declare template<typename T> void complexfun(std::vector<T>) before the function that is supposed to be using it.

Just swap the order of those function templates so you get:

template<typename T>                   // this function template
void complexfun(std::vector<T>) {      
    std::cout << "2 end" << std::endl;
}

template<typename T, typename... Args> // ...before this function template
void complexfun(T, Args... args) {     
    std::cout << "1 ";
    complexfun(args...);
}

Demo

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Thanks! I would have never dreamt of changing the order of the functions. For future reference, a full solutions which works with `complexfun(x1, vint, x1, x1)` requires both the variadic functions to be declared before the definition of any of the two – Ivo Mar 31 '22 at 16:01
  • @Ivo You're welcome! Yes, if you for example have circular dependencies you can make it work by separating the declarations and deinitions. That also makes it possible to declare and define in arbitrary order. [example](https://godbolt.org/z/zf35PP946) – Ted Lyngmo Mar 31 '22 at 16:48