【问题标题】:Is this code valid? Works with gcc, don't work with clang [duplicate]此代码有效吗?适用于 gcc,不适用于 clang [重复]
【发布时间】:2014-12-21 09:09:47
【问题描述】:

以下最小代码可以在 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 有 bug 吗?

【问题讨论】:

  • 我认为@Namfuak 发现了一个重复:表达式b * a 尝试实例化运算符函数模板,这导致T 被推断为float,并且产生的特化(运算符function) 是格式错误的,因为它没有类/枚举类型的参数或其引用。这似乎不是替换失败,即硬错误。参见 [over.oper]/1 和 /6。
  • 我试图进一步最小化您的代码并删除了 A::operator*... 但注意到这会阻止 GCC 编译代码。有趣...
  • @LightnessRacesinOrbit 错误消息很有趣:它暗示替换,这不应该是问题。没有从Afloat 的有效转换是重载解决问题(在模板实例化后选择可行的重载)。

标签: c++ templates g++ clang clang++


【解决方案1】:

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

那么编译器做了什么?它首先尝试找到所有可以使用的operator* 函数(之后,它会尝试确定要使用哪个函数)。 可以使用的operator* 函数之一是::operator*&lt;float&gt;。但是等等,::operator*&lt;float&gt; 是什么?这是float *(float, const float&amp;)!而我们不能这样做!您不能为原始类型重载运算符(想象一下,如果重载 int +(int, int) 会造成混乱,所以您让 1 + 2 做一些与每个人期望的完全不同的事情)。

此时,程序格式不正确。编译器甚至尝试实例化::operator*&lt;float&gt; 的事实使整个程序无效。所以,我们能做些什么?告诉编译器具体做什么:

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*&lt;float&gt;

This is explained by @dyp in the commentsby @TemplateRex in the duplicate question。然而,在我理解他们的意思之前,我不得不多次阅读他们的回复。我试图在这个答案中简化事情。如果我可以改进它,请告诉我!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-26
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 2014-08-23
    • 2021-02-17
    相关资源
    最近更新 更多