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

标签 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)考虑更好的比赛?我怎样才能实现我想要的?

最佳答案

事实上,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 推导会成功(TCSizex<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/

相关文章:

c++ - 在 CSplitterWnd 中设置事件面板

C++ -& CRTP 。类型删除与多态性

c++ - LNK2001 和 LNK2019 错误 - DirectX 未解析的外部符号

javascript - Backbone js。如何在backbone js中将数据从 View 传递到模板

css - 如何在 Joomla 2.5 模板中组织 css 文件?

c++ - 不明白 "#define for if-statement"

c++ - 使用 hash_map<vector<int>,string> 失败,为什么?

javascript - 如何在 Rails 中使用 CoffeeScript?

javascript - Meteor - 在模板加载时将类添加到 DOM 元素

c++ - clang 的 -Wweak-vtables 是什么意思?