此代码有效吗?适用于 gcc,不要与 clang 合作

Is this code valid? Works with gcc, don't work with clang

本文关键字:clang 合作 gcc 有效 代码 适用于      更新时间:2023-10-16

以下最小代码在g++上编译,但不会在clang++上编译:

template<class T>
T operator*(float a, const T& b)
{
    return b * a;
}
struct A{
    A operator*(float b) const
    {
        A a;
        return a;
    }
};
int main()
{
    A a;
    2.0f * a;
}

这是我得到的错误:

$ clang++ test.cpp 
test.cpp:2:3: error: overloaded 'operator*' must have at least one parameter of
      class or enumeration type
T operator*(float a, const T& b)
  ^
test.cpp:4:11: note: in instantiation of function template specialization
      'operator*<float>' requested here
        return b * a;
                 ^
test.cpp:18:10: note: in instantiation of function template specialization
      'operator*<A>' requested here
    2.0f * a;
         ^
1 error generated.

Clang 3.5版。这个代码有效吗?Clang上有虫子吗?

2.0f * a;实例化::operator*<A>。在该函数中,我们有表达式b * a,如果您查看(简化的)类型,它就是A * float。此时,编译器需要做出选择。*应该是全局函数::operator*<float>(因为右边的参数是float),还是应该是A::operator*?对我们人类来说,很明显它应该是A::operator*,但从编译器的角度来看,这还不清楚。

那么编译器是做什么的呢?它首先试图找到可以使用的所有operator*函数(之后,它试图准确地确定要使用哪一个)。可以使用的operator*函数之一是::operator*<float>。等等,什么是::operator*<float>?是float *(float, const float&)!我们不能那样做!不能为基元类型重载运算符(想象一下,如果重载int +(int, int),使1 + 2做一些与每个人期望的完全不同的事情,会出现混乱)。

在这一点上,程序是不规范的。编译器甚至试图实例化::operator*<float>这一事实本身就使整个程序失效。那么我们能做什么呢?告诉编译器该做什么:

template<class T>
T operator*(float a, const T& b)
{
    // This prevents the compiler from instantiating ::operator*<float>
    return b.operator*(a);
    // The above is meant to illustrate how the fix needs to work: it needs
    // to avoid instantiating ::operator*<float>. Other methods can be used
    // (like SFINAE) that might be more elegant (check out Walter's answer
    // in the duplicate: https://stackoverflow.com/a/18596809/1287251), but
    // in the end any solution used must avoid ::operator*<float>.
}
struct A{
    A operator*(float b) const
    {
        A a;
        return a;
    }
};
int main()
{
    A a;
    2.0f * a;
}

简而言之,为了回答以下问题:不,代码无效。必须防止编译器尝试实例化::operator*<float>

@dyp在评论中对此进行了解释,@TemplateRex在重复问题中对此进行解释。然而,我不得不多次阅读他们的回复,才明白他们的意思。我试着用这个答案来简化事情。如果我能改进,请告诉我!