我有以下代码:
#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;
}
当我这样做时:
Logger() << CSize();
编译器正在尝试实例化 Logger& operator<<(const T& value)
过载,当然会失败并返回 static_assert
.为什么不是 Logger& operator<<(const CSizex<T>& size)
考虑更好的比赛?我怎样才能实现我想要的?
最佳答案
事实上,const CSizex<T>&
是一个精确匹配,因为它是身份转换; [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).
从导致错误的模板中实例化的特化也是完全匹配的: [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.
但是,第一个模板比第二个模板更专业。在从原始模板和转换后的模板的参数和参数类型中删除引用和 cv 限定符之后,我们为一个独特的类型 Unique
, 根据另一个模板的原始模板的参数推导转换模板的参数:Unique
根据 CSizex<T>
推导,这会导致演绎失败(因为 Unique
不是 CSizex
的特化),而 CSizex<Unique>
根据 T
推导会成功(T
是 CSizex<Unique>
本身)。所以第一个模板更专业,因此应该通过部分排序来选择。
Clang compiles this correctly. GCC 4.9.0也是如此.也许您将问题简化为不再反射(reflect)错误的代码。
更新:
现在,我们考虑
template <typename T>
Logger& operator<<(const CSizex<T>& size); // #1
template <typename T>
Logger& operator<< (const T& value); // #2
对于#2,参数推导为CSize
所以参数是CSize const&
,而对于 #1,特化的参数是 CSize<int> const&
.
重载解决方案在上面的引用中明确指出:
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).
... 该转换是#2 的身份转换,但#1 的派生到基础转换。不难看出 #2 被选中是因为更好的排名 [over.best.ics]/6:
A derived-to-base Conversion has Conversion rank (13.3.3.1.1).
...并且身份转换具有完全匹配等级。
基本上将静态断言的条件移动到 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 .
关于c++ - 重载的模板方法未按预期解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26754107/