【问题标题】:Conversion operator: gcc vs clang转换运算符:gcc vs clang
【发布时间】:2021-11-30 10:45:44
【问题描述】:

考虑以下代码 (https://godbolt.org/z/s17aoczj6):

template<class T>
class Wrapper {
    public:
    explicit Wrapper(T t): _value(t) {}

    template<class S = T>
    operator T() { return _value; }
    private:
    T _value;
};

auto main() -> int
{
    auto i = int{0};
    auto x = Wrapper<int>(i);
    return x + i;
}

它可以使用 clang 编译,但不能使用 gcc(所有版本)。 删除template&lt;class S = T&gt; 时,它在 gcc 中有效。 这段代码格式错误还是某个编译器错误?

gcc 中的错误是error: no match for 'operator+' (operand types are 'Wrapper&lt;int&gt;' and 'int') return x + i;

我想要转换为T。此示例中不需要模板,但在一个非最小示例中,我想使用 SFINAE,因此这里需要一个模板。

【问题讨论】:

  • 您可以考虑添加language-lawyer 标签。我没有这样做,因为它稍微改变了问题的范围。
  • 为什么需要这个额外的模板?没有它,它无处不在godbolt.org/z/53vq5Ycd7
  • @MarekR SFINAE 可能需要在运算符的两种不同实现之间进行选择
  • 如果可能的话,您似乎需要您的操作员执行转换。所以应该是operator S(),而不是operator T()。由于无法为编译器推断出S,这可能是错误的原因。请提供错误文本
  • 构造函数中没有转换。它在x+i 操作中。

标签: c++ language-lawyer implicit-conversion


【解决方案1】:

当您拥有x + i 时,由于x 属于类类型,因此重载决议开始:

标准中的具体细节([over.match.oper]p2)

如果任一操作数的类型是类或枚举,则可能会声明实现此运算符的用户定义的运算符函数,或者可能需要用户定义的转换将操作数转换为适合的类型内置运算符。 在这种情况下,重载决策用于确定要调用哪个运算符函数或内置运算符来实现运算符。

内置候选项在第 3.3 段中定义:

对于operator ,、一元operator &amp;operator -&gt;,内置候选集为空。对于所有其他运算符,内置候选函数包括 [over.built] 中定义的所有候选运算符函数,与给定运算符相比,

  • 具有相同的操作员名称,并且
  • 接受相同数量的操作数,并且
  • 接受可以根据 [over.best.ics] 将给定操作数转换为的操作数类型,并且
  • 不具有与任何非函数模板特化的非成员候选相同的参数类型列表。

根据 [over.built]p13,内置候选函数可能包括:

对于每一对类型 L 和 R,其中 L 和 R 都是浮点或提升的整数类型,存在形式为的候选运算符函数

LR      operator*(L, R);
...
LR      operator+(L, R);
...
bool    operator>=(L, R);

其中 LR 是类型 L 和 R 之间通常算术转换 ([expr.arith.conv]) 的结果。

所以有一个内置函数int operator+(int, int)

至于有哪些可能的隐式转换序列:

[over.best.ics]p3:

格式良好的隐式转换序列是以下形式之一:

  • 标准转换序列,
  • 用户定义的转换序列,或
  • 省略号转换序列。

这里使用了用户定义的转换序列,由[over.ics.user]定义:

用户定义的转换序列由初始标准转换序列和用户定义的转换 ([class.conv]) 以及第二个标准转换序列组成。

(这里,两个标准转换序列都是空的,可以使用用户定义的转换为int

因此,当检查 int operator+(int, int) 是否是内置候选对象时,这是因为您的类类型和 int 之间存在转换。


至于实际的重载解析,来自[over.match.oper]:

  1. 用于某些运算符 @ 的重载解决方案的候选函数集是成员候选函数、非成员候选函数、内置候选函数以及该运算符 @ 的重写候选函数的并集。
  2. 参数列表包含运算符的所有操作数。 根据[over.match.viable]和[over.match.best]从候选函数集合中选出最佳函数。

int operator+(int, int) 显然是最好的匹配,因为它不需要对第二个参数进行转换,而只需要对第一个参数进行用户定义的转换,因此它击败了其他候选者,例如 long operator+(long, int)long operator+(int, long)


您可以看到内置候选集为空的问题,因为 GCC 错误报告有 no 个可行的候选集。如果你有:

auto add(int a, int b) -> int
{
    return a + b;
}

auto main() -> int
{
    auto i = int{0};
    auto x = Wrapper<int>(i);
    return add(x, i);
}

它现在可以使用 GCC 正常编译,因为 ::add(int, int) 被认为是候选者,即使它与内置运算符 int operator+(int, int) 应该没有什么不同。

如果你有:

    template<class S = T>
    operator S() { return _value; }  // Can convert to any type

Clang 现在有错误:

<source>:16:14: error: use of overloaded operator '+' is ambiguous (with operand types 'Wrapper<int>' and 'int')
    return x + i;
           ~ ^ ~
<source>:16:14: note: built-in candidate operator+(float, int)
<source>:16:14: note: built-in candidate operator+(double, int)
<source>:16:14: note: built-in candidate operator+(long double, int)
<source>:16:14: note: built-in candidate operator+(__float128, int)
<source>:16:14: note: built-in candidate operator+(int, int)
<source>:16:14: note: built-in candidate operator+(long, int)
<source>:16:14: note: built-in candidate operator+(long long, int)
<source>:16:14: note: built-in candidate operator+(__int128, int)
<source>:16:14: note: built-in candidate operator+(unsigned int, int)
<source>:16:14: note: built-in candidate operator+(unsigned long, int)
<source>:16:14: note: built-in candidate operator+(unsigned long long, int)
<source>:16:14: note: built-in candidate operator+(unsigned __int128, int)

(请注意,此错误消息不包括第二个参数的转换,但由于永远不会选择这些,因此它们可能不被视为优化)

而且 GCC 仍然说根本没有候选者,尽管所有这些内置候选者都存在。

【讨论】:

  • 总之,GCC 应该考虑这个候选人,但它没有(因此它是一个错误)?
  • @Henk 是的。我还发现这里发生了完全相同的问题:stackoverflow.com/q/49653048 和类似的问题:stackoverflow.com/q/25100855
  • 有趣!但是我还没有找到一个错误报告,代码很简单,我有一个例子,我不知道除了模板转换运算符之外的任何其他解决方案。应该报告吗?
  • 这里有一个类似的问题:gcc.gnu.org/bugzilla/show_bug.cgi?id=85250T 依赖时,gcc 似乎很难考虑模板化的operator T(较短的示例:godbolt.org/z/79rjTovn9
猜你喜欢
  • 1970-01-01
  • 2017-02-22
  • 2014-11-02
  • 1970-01-01
  • 1970-01-01
  • 2016-09-15
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多