xmllmx
xmllmx

Reputation: 42379

Why does recursive variadic templates not work as expected?

#include <type_traits>

template<typename T>
T f(T&& a, T&& b)
{
    return a + b;
}

template<typename T, typename... Args>
T f(T&& a, T&& b, Args&&... args)
{
    return f(a, f(b, std::forward<Args>(args)...));
}

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

VS 2015 outputs: error C2672: 'f': no matching overloaded function found

Why does it not work as expected?

Upvotes: 1

Views: 238

Answers (1)

ildjarn
ildjarn

Reputation: 62995

The problem is that you aren't forwarding a or b when recursively calling f, which results in attempting to call the binary overload with a : int and b : int&. Here's a first step:

template<typename T>
T f(T&& a, T&& b) {
    return a + b;
}

template<typename T, typename... Args>
T f(T&& a, T&& b, Args&&... args) {
    return f(std::forward<T>(a), f(std::forward<T>(b), std::forward<Args>(args)...));
}

Now the problem is that all arguments passed in must have the same value category and any lvalue argument will cause an error, e.g. int i = 2; f(1, i, 3); will fail. To fix this..:

template<typename T, typename U>
typename std::decay<T>::type
f(T&& a, U&& b) {
    return a + b;
}

template<typename T, typename U, typename... Args>
typename std::decay<T>::type
f(T&& a, U&& b, Args&&... args) {
    return f(std::forward<T>(a), f(std::forward<U>(b), std::forward<Args>(args)...));
}

which can then be simplified to:

template<typename T, typename U>
typename std::decay<T>::type
f(T&& a, U&& b) {
    return a + b;
}

template<typename T, typename... Args>
typename std::decay<T>::type
f(T&& a, Args&&... args) {
    return f(std::forward<T>(a), f(std::forward<Args>(args)...));
}

Upvotes: 6

Related Questions