C++ C++;运算符重载和关联的命名空间

C++ C++;运算符重载和关联的命名空间,c++,namespaces,operator-overloading,clang,argument-dependent-lookup,C++,Namespaces,Operator Overloading,Clang,Argument Dependent Lookup,下面的简化示例在gcc和visualstudio中编译,但在clang中失败 namespace N { struct A {}; template <typename T> double operator+ (T a, double d) {return d;} template <typename T> double operator+ (double d, T a) {return d;} } void test() {

下面的简化示例在
gcc
visualstudio
中编译,但在
clang
中失败

namespace N
{
    struct A {};

    template <typename T>
    double operator+ (T a, double d) {return d;}

    template <typename T>
    double operator+ (double d, T a) {return d;}
}

void test()
{
    N::A a;
    double x;

    double y = a + x;
    double z = x + a;
}

当然,这个例子是从现实生活中的代码简化而来的。其目的是在名称空间N中定义的任何类都有一个重载运算符+和一个double。

它可能会抱怨,因为
T
可能不是该定义中的类。并且不允许为算术类型IIRC重新定义标准的
运算符+
。例如,在您的示例中,没有任何东西限制
T
成为
N::A

添加
typename=std::enable_,如果_t
似乎可以解决此问题。VisualStudio和GCC对此限制可能更为宽松/懒惰

namespace N
{
    struct A {};

    template <typename T, typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}>>
    double operator+ (T a, double d) {return d;}

    template <typename T, typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}>>
    double operator+ (double d, T a) {return d;}
}

 void test()
 {
    N::A a;
    double x;

    double y = a + x;
    double z = x + a;
 }
名称空间N
{
结构A{};
模板
双运算符+(ta,d){返回d;}
模板
双运算符+(双d,ta){返回d;}
}
无效测试()
{
N::A;
双x;
双y=a+x;
双z=x+a;
}

这是由两个不同的CWG问题引起的:和

首先,CWG 1391。遇到
x+a
时,通常的名称查找会在其他重载中找到

template <typename T> double operator+ (T, double);
运算符模板的模板参数推导成功,
生成签名
运算符从该错误中,看起来ADL正在查找它。不确定它在抱怨什么Though注意,您的测试程序有未定义的行为,因为
x
未初始化。@KerrekSB您正在复制,因为您使用的是gcc,而您可以编译它。在实例化之前,我认为这个问题不应该引起关注。例如,如果您发出叮当声,则不会抱怨。@0x499602D2 True。这里发生了一些奇怪的事情。如果您在另一个加法中离开,仍然会弹出该错误。错误消息(原始消息和coliru消息)表明,在编译失败的情况下,它正在使用
T=double
实例化函数
在这里请求的函数模板专门化'N::operator+'实例化中
是的,我认为clang在执行重载解析时不小心实例化了候选函数。根据T.C的回答,我认为enable_if不能解决clang中的问题。为什么
operator+(double,double)
在OP的情况下是否可以推导?@KerrekSB给定
模板双运算符+(T,double);双x;N::A
x+a
T
是从lhs中推导出来的,即
double
。当然,对于rhs,没有从
N::A
double
的转换,但是模板参数推导并不重要;剩下的就是重载解析了。标准中有趣的漏洞。谢谢你的解释。我想clang是对的,GCC/VC也会是对的。@T.C:哦,当然,我在考虑另一个函数。是的,有道理。这本应该是SFINAE上下文,但不是。我想CWG问题将是一个DR对抗11的问题——没有人希望这种行为依赖于语言版本。谢谢你的解释。根据这个答案,我只有两种选择:a)为命名空间N中的每个类提供重载运算符,或b)在命名空间N中的所有类中使用CRTP,然后根据公共基类定义运算符。我说得对吗?
template <typename T> double operator+ (T, double);
  struct A { operator int(); };
  template<typename T> T operator<<(T, int);
  void f(A a) { 1 << a; }