由于删除了模板化的左值转换运算符,可能导致clang或gcc错误? [英] Possible bug in clang or gcc because of deleted templated lvalue conversion operator?

查看:47
本文介绍了由于删除了模板化的左值转换运算符,可能导致clang或gcc错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是对问题的跟踪.我玩过这段代码,并使用了clang干线和gcc干线:

This is a follow-up of this question. I played with this code and used clang trunk and gcc trunk:

struct A
{   
};

struct T
{
    A a;
    operator A() { return a; }
    
    template <typename T> operator const T&() = delete;
};

struct C
{   
    A a;
};

int main()
{
    C c;
    T t;

    c.a = t;
}

演示

Clang没什么可抱怨的,但是gcc有:

Clang has nothing to complain, but gcc has:

<source>: In function 'int main()':
<source>:23:11: error: use of deleted function 'T::operator const T&() [with T = A]'
   23 |     c.a = t;
      |           ^
<source>:10:27: note: declared here
   10 |     template <typename T> operator const T&() = delete;
      |  

                     ^~~~~~~~

那么,哪个编译器是正确的,哪个是错误的?

So, which compiler is right and which is wrong?

我希望看到gcc是错误的,但是我该如何克服该错误?

I would like to see gcc is wrong, but how can I overcome the error?

推荐答案

Clang和GCC都错了,但至少GCC停止了编译,因此略胜一筹.

Both Clang and GCC are wrong, but at least GCC stops compilation so it's marginally better.

A 是类类型,因此分配给它通过 operator = 重载.您没有提供一个,所以编译器提供了两个效果.

A is a class type, and as such assigning to it passes through an operator= overload. You did not provide one, so the compiler provides two to the effect of

A& operator=(A const&) = default;
A& operator=(A&&) = default;

该引用参数是需要从表达式 c.a = t 中的 t 初始化的.对于 operator = 而言,引用可以明确地绑定.

That reference argument is what needs to get initialized from t in the expression c.a = t. And for either operator=, the reference can be bound unambiguously.

[dcl.init.ref]

5 对类型的引用"cv1 T1"由以下表达式初始化键入"cv2 T2",如下所示:

5 A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:

  • 如果引用是左值引用和初始值设定项表达式

  • If the reference is an lvalue reference and the initializer expression

  • [...]
  • 具有类类型(即T2是类类型),其中T1与T2无关,并且可以转换为类型的左值"cv3 T3",其中"cv1 T1"与"cv3 T3"参考兼容(本通过列举适用的转换来选择转换功能([over.match.ref])并通过选择最佳的一种重载分辨率),

然后将引用绑定到...的左值结果在第二种情况下(或在任何一种情况下,转换为适当的对象的基类子对象).

then the reference is bound ... to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).

否则,如果初始化表达式为

Otherwise, if the initializer expression

  • [...]
  • 具有类类型(即T2是类类型),其中T1与T2无关,并且可以转换为右值或函数类型为"cv3 T3"的左值,其中"cv1 T1"与"cv3 T3"(请参阅​​[over.match.ref]),

然后...第二种情况下的转换结果称为转换后的初始值设定项.如果转换后的初始值设定项是prvalue,其类型T4调整为类型"cv1 T4"([conv.qual]),并且应用临时物化转换([conv.rval]).在任何在这种情况下,引用将绑定到生成的glvalue(或适当的基类子对象).

then ... the result of the conversion in the second case is called the converted initializer. If the converted initializer is a prvalue, its type T4 is adjusted to type "cv1 T4" ([conv.qual]) and the temporary materialization conversion ([conv.rval]) is applied. In any case, the reference is bound to the resulting glvalue (or to an appropriate base class subobject).

关于为这两种情况建立候选集的主题,标准说

Where on the subject of building that candidate set for these two cases, the standard says

[over.match.ref]

1 在[dcl.init.ref],引用可以直接与将转换函数应用于的结果绑定初始化表达式.重载分辨率用于选择要调用的转换函数.假设引用cv1 T"是要初始化的引用的类型,而"cv S"是类型初始化表达式的表达式,S为类类型,候选功能选择如下:

1 Under the conditions specified in [dcl.init.ref], a reference can be bound directly to the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that "reference to cv1 T" is the type of the reference being initialized, and "cv S" is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

  • 考虑S的转换函数及其基类.那些未隐藏在S中的非显式转换函数并产生类型对cv2 T2的左值引用"(在初始化左值引用或函数右值引用)或"cv2 T2"或对cv2 T2的右值引用"(初始化右值引用或对函数的左值引用),其中"cv1 T"为候选功能与"cv2 T2"具有参考兼容性.为了直接初始化,即没有隐藏在S内并且产生类型对cv2 T2的左值引用"(当将左值引用或右值引用初始化为函数)或对cv2 T2的右值引用"(初始化右值引用时或对函数的左值引用),其中T2与T的类型相同或可以通过资格转换转换为T型候选函数.

该项目符号需要一些工作才能正确解析,但它基本上描述了可能在此处适用的两种不相交的情况之一:

That bullet needs a bit of work to parse correctly, but it basically describes one of two disjoint cases that may apply here:

  1. 对T的左值引用的初始化
    • 候选函数是产生对cv2 T2的左值引用"的函数.
    候选函数是那些产生"cv2 T2"的函数.或对cv2 T2的右值引用".
  • The candidate functions are those that yield "cv2 T2" or "rvalue reference to cv2 T2".

对于 operator =(A const&),我们处于情况#1,并且只有合成的 operator A const&()作为唯一候选者.对于 operator =(A&&),它的情况是#2,而非模板的 operator A()是唯一的候选者.无论哪种方式,我们都有一个明确的隐式转换序列,该序列具有用户定义的转换,该转换绑定了 operator = 的引用参数.

For operator=(A const&) we are in case #1, and have a synthesized operator A const&() as the only candidate. For operator=(A&&) its case #2, and the non-template operator A() is the only candidate. Either way, we have an unambiguous implicit conversion sequence with a user-defined conversion that binds the reference parameter of either operator=.

但是根据 [over.match.best] .根据 [over.ics.rank] .

But now neither operator= is a better viable function than the other according to the rules in [over.match.best]. And neither conversion is better according to the partial ordering in [over.ics.rank].

这意味着由于对 operator = 的模糊调用,程序应被声明为格式错误.但是,GCC和Clang都出错了(虽然不是MSVC) 1 .Clang支持 A& 重载,而GCC支持 A const& ,并发出有关使用已删除转换功能的诊断信息.但这不是由于任何标准的强制性行为.理想情况下,他们俩都应报告对 operator = 的呼叫是模棱两可的.

This means that the the program should be declared ill-formed on account of an ambiguous call to operator=. However, GCC and Clang both err (not MSVC though)1. Clang favors the A&& overload, while GCC goes for A const& and issues a diagnostic for the use of a deleted conversion function. But this isn't due to any standard mandated behavior. Ideally, they should both report the call to operator= is ambiguous.

1 - 查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆