I have the following code:
#include <iostream>
#include <type_traits>
template <typename T> struct CSizex {
};
struct CSize : public CSizex<int>
{
};
struct Logger
{
template <typename T>
Logger& operator<< (T& value)
{
return *this << const_cast<const T & >(value);
}
template <typename T> Logger& operator<<(const CSizex<T>& size)
{
std::cout << __FUNCSIG__;
return *this;
}
template <typename T>
Logger& operator<< (const T& value)
{
static_assert(std::is_arithmetic<T>::value || std::is_integral<T>::value || std::is_enum<T>::value, "This method is only meant for arithmetic types");
std::cout << __FUNCSIG__;
return *this;
}
};
int main()
{
CSize size;
Logger() << CSize();
return 0;
}
When I do this:
Logger() << CSize();
the compiler is trying to instantiate Logger& operator<<(const T& value) overload, which, of course, fails with static_assert. Why isn't Logger& operator<<(const CSizex<T>& size) considered a better match? How can I implement what I want?
In a fact, const CSizex<T>& is an exact match as it is an identity conversion; [over.ics.ref]/1:
When a parameter of reference type binds directly to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1).
The specialization instantiated from the template that causes the error is also an exact match: [over.ics.user]/4:
A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a copy/move constructor (i.e., a user-defined conversion function) is called for those cases.
However, the first template is more specialized than the second. After removing references and cv-qualifiers from the parameters and argument types of both the original and the transformed templates we then, for a unique type Unique, deduce the argument of the transformed template against the parameter of the original template of the other template: Unique is deduced against CSizex<T>, which yields a deduction failure (since Unique is no specialization of CSizex), while CSizex<Unique> deduced against T would be successful (with T being CSizex<Unique> itself). So the first template is more specialized and should thus be selected by partial ordering.
Clang compiles this correctly. So does GCC 4.9.0. Perhaps you reduced your problem to a code that does not reflect the mistake anymore.
Now, we consider
template <typename T>
Logger& operator<<(const CSizex<T>& size); // #1
template <typename T>
Logger& operator<< (const T& value); // #2
For #2, the argument is deduced as CSize so the parameter is CSize const&, while for #1 the parameter of the specialization is CSize<int> const&.
Overload resolution clearly states in the above quote:
When a parameter of reference type binds directly to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1).
... that the conversion is an identity conversion for #2 but a derived-to-base conversion for #1. It is not hard to see that #2 is selected due to the better rank, [over.best.ics]/6:
A derived-to-base Conversion has Conversion rank (13.3.3.1.1).
... and identity conversions have Exact Match rank.
Basically it should suffice to move the condition of the static assertion into enable_if:
struct Logger
{
template <typename T>
Logger& operator<<(const CSizex<T>& size)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value
|| std::is_integral<T>::value
|| std::is_enum<T>::value, Logger&>::type
operator<<(const T& value)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
};
Demo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With