Reputation: 4493
Imagine this code:
#include <iostream>
void PrintInternal() {
std::cout << std::endl;
}
template <typename T, typename...ARGS>
void PrintInternal(const T& head, const ARGS&...rest) {
std::cout << head << " ";
PrintInternal(rest...);
};
template <typename...ARGS>
void PrintInternal(const double& head, const ARGS&...rest) {
std::cout << "DBL!!! " << head << " ";
PrintInternal(rest...);
}
template <typename...ARGS>
void Print(const ARGS&...args) {
PrintInternal(args...);
}
int main() {
Print(1.1, 2, 3.3, 4);
Print(0, 1.1, 2, 3.3, 4);
return 0;
}
First Print
outputs:
DBL!!! 1.1 2 3.3 4
My expectations were, that it would output DBL!!! before 3.3 or no DBL!!! at all. But why one???
Second Print
outputs:
0 1.1 2 3.3 4
Why there is no DBL!!! output like at all, if we had one in first example.
And how to achieve, that for each double
I will output something different without partial specialization? I've thought, that simple overloading should be ok...
Link to cpp.sh to view compilation results -> http://cpp.sh/42cz
Upvotes: 3
Views: 81
Reputation: 217810
You have problem of visibility, you may fix it by forward declaration:
template <typename...ARGS> void PrintInternal(const double& head, const ARGS&...rest);
template <typename T, typename...ARGS>
void PrintInternal(const T& head, const ARGS&...rest) {
std::cout << head << " ";
PrintInternal(rest...);
}
template <typename...ARGS>
void PrintInternal(const double& head, const ARGS&...rest) {
std::cout << "DBL!!! " << head << " ";
PrintInternal(rest...);
}
Simpler would be to have the specific only for the simple print, and have the recursion apart, something like:
void printSingle(double d)
{
std::cout << "DBL!!! " << d << " ";
}
template <typename T>
void printSingle(const T& t)
{
std::cout << t << " ";
}
template <typename...ARGS>
void Print(const ARGS&...args) {
const int dummy[] = {0, (printSingle(args), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
std::cout << std::endl;
}
Upvotes: 0
Reputation: 303337
Lookup for PrintInternal()
is going to find two types of functions:
In this case, all of our arguments are fundamental types, so there aren't any associated namespaces. That makes things easier. So when we start with:
#include <iostream>
void PrintInternal() { // #1
std::cout << std::endl;
}
template <typename T, typename...ARGS>
void PrintInternal(const T& head, const ARGS&...rest) { // #2
std::cout << head << " ";
PrintInternal(rest...); // <== (*)
};
template <typename...ARGS>
void PrintInternal(const double& head, const ARGS&...rest) { // #3
std::cout << "DBL!!! " << head << " ";
PrintInternal(rest...);
}
That marked call to PrintInteral()
only has two candidates: the nullary function (#1) and itself (#2). The other one, the more specialized PrintInteral()
that takes a const double&
(#3) isn't visible yet, so is never considered as a candidate. It's not that #2 was preferred to #3, it's just that it was the only option.
If you flip the ordering of the two overloads, then you'd have a different problem - you wouldn't be able to find #2!
This gives you a few options:
PrintSingle()
, which is easier to do.Introduce another argument solely for the purpose of that second bullet point on top applying. Just a dummy argument that exists just to do name lookup with ADL. This solution is sometimes necessary, but always confusing:
namespace N {
struct adl { };
void PrintInternal(adl ) {
std::cout << std::endl;
}
template <typename T, typename...ARGS>
void PrintInternal(adl, const T& head, const ARGS&...rest) {
std::cout << head << " ";
PrintInternal(adl{}, rest...);
}
template <typename...ARGS>
void PrintInternal(adl, const double& head, const ARGS&...rest) {
std::cout << "DBL!!! " << head << " ";
PrintInternal(adl{}, rest...);
}
}
template <typename...ARGS>
void Print(const ARGS&...args) {
PrintInternal(N::adl{}, args...);
}
Upvotes: 6