10

当我编译下面的代码片段时,我得到了一个编译器错误,但是 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;
}

报告的错误如下:

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.

这是关于未使用的运算符功能。相反,如果我写的是 10 > d 而不是 d > 10,那么我会得到关于另一个 operator > 函数的相同错误。以上在 gcc 4.4.6 和 VS2012 下编译良好。我的错误是什么?

谢谢你。

4

3 回答 3

7

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

回想一下名称查找、参数推导和过载解决的三位一体。第一步找到两个重载的operator>. 第二步推导出每个版本的模板参数。您可能会认为第二个重载将成为 SFINAE 规则 (14.8.2) 的牺牲品,因此只有第一个重载可以保留到第三步。但是,没有替换失败(例如缺少嵌套的 typedef),而是非法构造(参见前面提到的 13.5/1)。这本身会使程序格式错误(14.3/6)

6 如果模板参数的使用在模板特化的实例化中产生了格式错误的构造,则程序格式错误。

在 14.8.3 中提到,对推导参数的检查发生在重载决议之前,因此您的首选运算符没有机会被选中。

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

于 2013-09-03T20:09:52.270 回答
0

我必须承认,我真的不知道为什么 clang 在这里抱怨,它看起来像一个错误(编译器的)。顺便说一句,clang 3.3 也出现了这个问题。

您可以使用 SFINAE 抑制它:

template<typename L>
typename std::enable_if<std::is_class<L>::value || std::is_enum<L>::value,
                        Greater<L, Const<int>>>::type
operator > (L lhs, int rhs) { 
    return Greater<L, Const<int> >(lhs, Const<int>(rhs));
}

template<typename R>
typename std::enable_if<std::is_class<R>::value || std::is_enum<R>::value,
                        Greater<Const<int>,R>>::type
operator > (int lhs, R rhs) { 
    return Greater<Const<int>, R>(Const<int>(lhs), rhs);
}
于 2013-09-03T16:06:17.130 回答
0

这在我看来像是 g++ 和 VS 中的一个错误。在您的示例中,您的类型Rint(因为右侧操作数是int)。然后,这使得函数的签名与intsGreater<Const<int>, R> operator > (int lhs, int rhs) 的内置函数(参数)签名相同operator<。请注意,在决定使用哪个模板时,它必须考虑两个模板(并尝试为每个模板分别推断类型)operator>:它不能只查看其中一个并决定忽略另一个。

于 2013-09-03T16:58:59.223 回答