重载的模板方法未按预期解析

时间:2014-11-05 09:55:42

标签: c++ templates

我有以下代码:

#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)被认为不是更好的匹配?我该如何实现我想要的?

1 个答案:

答案 0 :(得分:2)

事实上,const CSizex<T>&完全匹配,因为它是身份转换; [over.ics.ref] / 1:

  

当引用类型的参数直接绑定到参数时   表达式,隐式转换序列是标识   转换,除非参数表达式的类型是a   派生类的参数类型,在这种情况下是隐式的   转换序列是派生到基础的转换(13.3.3.1)。

从导致错误的模板实例化的特化也是完全匹配: [over.ics.user] / 4:

  

将类类型的表达式转换为相同的类类型   给出精确匹配等级,以及类表达式的转换   类型到该类型的基类被赋予转换排名,尽管如此   复制/移动构造函数(即用户定义的)的事实   为这些情况调用转换函数。

但是,第一个模板比第二个模板更专业。从原始模板和转换后的模板的参数和参数类型中删除引用和cv限定符后,对于唯一类型Unique,可以根据原始模板的参数推导出已转换模板的参数。其他模板:Unique是针对CSizex<T>推断的,这会导致扣减失败(因为Unique不是CSizex的特化),而CSizex<Unique>是针对{T推断的1}}会成功(T本身就是CSizex<Unique>)。所以第一个模板更专业,因此应该通过部分排序来选择  Clang compiles this correctly. GCC 4.9.0也是如此。也许你将问题简化为不再反映错误的代码。

<小时/>

更新

现在,我们考虑

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&。 过载分辨率在上面的引用中明确说明:

  

当引用类型的参数直接绑定到参数时   表达式,隐式转换序列是标识   转换,除非参数表达式的类型是a   派生类的参数类型,在这种情况下是隐式的   转换序列是派生到基础的转换(13.3.3.1)。

...转换是#2的身份转换,但#1的派生到基本转换。由于排名较高,不难看出#2被选中,[over.best.ics] / 6:

  

派生到基础的转换具有转换等级(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