zhenguoli
zhenguoli

Reputation: 2308

variadic template recursion with sizeof..., but compile error: no matching function

I write a variadic template to print all the arguments with recursion:

#include <iostream>
using std::ostream; using std::istream;
using std::cin; using std::cout; using std::endl;

template <typename T, typename... Args>
ostream &myprint(ostream &os, const T &t, const Args&... rest) {
    if (sizeof...(rest)) {
        os << t << ", ";
        return myprint(os, rest...);
    }
    else
        return os << t;
}

int main(int argc, char *argv[]) {
    myprint(cout, "hello");
    return 0;
}

But when I compiles it with g++ -std=c++1y, it complains:

error: no matching function for call to ‘myprint(std::ostream&)’
     return myprint(os, rest...);

In the function myprint, I have checked the value of sizeof...(rest). And when it is 0, it will not call myprint(os, rest...). So I don't know why it will call myprint(std::ostream&).

And I have also searched for the related question, I have found that it needs a base case. But why do I need a base case, and can't sizeof... work in a variadic template?

And for the simple indefinite recursive case:

template <typename T, typename... Args>
ostream &myprint(ostream &os, const T &t, const Args&... rest) {
    os << t << ", ";            // print the first argument
    return print(os, rest...);  // recursive call; print the other arguments
}

The code above can't be compiled at all for the same error.

Upvotes: 1

Views: 238

Answers (2)

songyuanyao
songyuanyao

Reputation: 172914

For the if statement you used, both the statement-true and statement-false must be valid, whether the condition yields to the result of true or false.

You can use constexpr if since C++17; when the value of condition is false, the statement-true will be discarded. e.g.

if constexpr (sizeof...(rest)) {
    os << t << ", ";
    return myprint(os, rest...);
}
else
    return os << t;

If you can't use C++17, you can add another template overload for the case that the number of arguments is only one, to stop the recursion, e.g.

template <typename T>
ostream &myprint(ostream &os, const T &t) {
    return os << t;
}

template <typename T, typename... Args>
ostream &myprint(ostream &os, const T &t, const Args&... rest) {
    os << t << ", ";
    return myprint(os, rest...);
}

LIVE

Upvotes: 4

Passer By
Passer By

Reputation: 21131

songyuanyao's answer explains why it's invalid and provides a solution for C++17. Alternatively, you could have a base case to myprint prior to that.

template <typename T>
ostream &myprint(ostream &os, const T &t) {
    return os << t;
}

template <typename T, typename... Args>
ostream &myprint(ostream &os, const T &t, const Args&... rest) {
    os << t << ", ";
    return myprint(os, rest...);
}

Upvotes: 2

Related Questions