模板化运算符重载的奇怪错误

Joh*_*ohn 10 c++ templates operator-overloading clang sfinae

当我编译以下代码片段时,我得到了一个带有clang的编译器错误,但是没有使用g ++/MSVC:

#include <string>

template<typename T> struct Const { 
    explicit Const(T val) : value(val) {}
    T value;
};

template<typename T> struct Var {
    explicit Var(const std::string &n) : name(n) {}

    std::string name;
};

template<typename L, typename R> struct Greater {
    Greater(L lhs, R rhs) : left(lhs), right(rhs) {}

    L left;
    R right;
};

template<typename L>
Greater<L, Const<int> > operator > (L lhs, int rhs) { 
    return Greater<L, Const<int> >(lhs, Const<int>(rhs));
}

template<typename R>
Greater<Const<int>, R> operator > (int lhs, R rhs) { 
    return Greater<Const<int>, R>(Const<int>(lhs), rhs);
}

Var<double> d("d");

int main() {
     d > 10;
     return 0;
}
Run Code Online (Sandbox Code Playgroud)

报告的错误如下:

error: overloaded 'operator>' must have at least one parameter of
      class or enumeration type
Greater<Const<int>, R> operator > (int lhs, R rhs) { 
                       ^
./val.h:31:24: note: in instantiation of function template specialization
      'operator><int>' requested here
Greater<Const<int>, R> operator > (int lhs, R rhs) { 
                       ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

这是关于未使用的操作员功能.相反,如果我写10> d而不是d> 10,那么我得到关于其他运算符>函数的相同错误.以上编译在gcc 4.4.6和VS2012下正常.我的错是什么?

谢谢.

Tem*_*Rex 7

Clang是对的:运算符重载至少需要一个类或枚举类型参数,否则程序格式不正确(13.5/1).要了解为什么会出现此错误,我们必须解析更多标准法律问题.

回想一下名称查找,参数推导和过载分辨率的三位一体.第一步发现两个重载operator>.第二步推导出每个版本的模板参数.您可能会认为第二次超载会成为SFINAE规则(14.8.2)的受害者,因此只有第一次过载才能存活到第三步.但是,没有替换失败(例如缺少嵌套的typedef),而是非法构造(参见前面提到的13.5/1).这本身就使得该计划形成不良(14.3/6)

6如果在模板专业化的实例化中使用template-argument会导致格式错误的构造,那么该程序就会形成错误.

在14.8.3中,提到对推断的参数的这种检查在重载解析之前发生,因此您的首选运算符没有被选中的机会.

作为C++ 03的解决方法,您可以operator>Var<T>类模板中定义两个朋友非模板.这些将作为具有一个类类型参数的非模板函数注入到周围(全局,在此示例中)命名空间中,因此不应发生上述错误.