Reputation: 16711
I tried to write a template class and output operator to it as following:
#include <iostream>
namespace N
{
template< typename ...types >
struct X
{
static_assert((sizeof...(types) != 0), "zero length");
X() = default;
X(X const &) = default;
X(X &&) = default;
template< typename ...args >
//explicit // does not matter
X(args &&...) { ; }
int value = 10;
};
template< typename ...types >
std::ostream &
operator << (std::ostream & out, X< types... > const & x)
{
return out << x.value;
}
} // namespace N
int main()
{
using namespace N;
X< float > /*const*/ x; // `const` does not matter
std::cout << x << std::endl;
return 0;
}
But static_assert
ion raised:
main.cpp:9:5: error: static_assert failed "zero length"
static_assert((sizeof...(types) != 0), "zero length");
^ ~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:32:23: note: in instantiation of template class 'N::X<>' requested here
std::cout << x << std::endl;
^
1 error generated.
If class template X
and operator <<
overloading defined in global namespace
then all is the same. I found out, that commenting using namespace N;
line and substitution X< float >
to N::X< float >
solves the problem.
How to explain such behaviour? What is the cause?
EDIT:
I find the solution: is to chage template parameters of operator <<
overloading as follows:
template< typename first, typename ...rest >
std::ostream &
operator << (std::ostream & out, X< first, rest... > const & x)
{
return out << x.value;
}
Splitting typename ..types
of the class is not nessesarily. Moreover, it is not desirable at all, due to extreme bloating of code as consequences.
Upvotes: 2
Views: 454
Reputation: 47418
A simple way to reproduce your issue:
int main()
{
using namespace N;
std::cout << std::endl;
}
In this case, candidate functions are all overloads of operator<<
from namespace std
, all member operator<<'s from std::ostream
, and your function template operator<<
from namespace N
.
13.3.1/7: "where a candidate is a function template, candidate function template specializations are generated using template argument deduction"
So, before overload resolution can begin, X<types...> const&
has to be deduced from std::endl
, which is an address of a template function. Address of a function is a function pointer type, and the only way to match N::X<types...> const&
to a pointer type is to deduce types...
as an empty list.
(substitution of course fails since there is no implicit conversion from any function pointer type to N::X<>
, which would have eliminated the overload quietly as non-viable, but the static assert is not in immediate context and is a hard error)
The moral of the story: using-directives are evil.
Upvotes: 5