繁体   English   中英

由于删除了模板化左值转换运算符,clang 或 gcc 中可能存在错误?

[英]Possible bug in clang or gcc because of deleted templated lvalue conversion operator?

这是这个问题的后续。 我玩过这段代码并使用了 clang 主干和 gcc 主干:

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 有:

<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;
      |  

                     ^~~~~~~~

那么,哪个编译器是对的,哪个是错的呢?

我想看看 gcc 是错的,但我该如何克服这个错误?

Clang 和 GCC 都是错误的,但至少 GCC 停止编译,所以它稍微好一点。

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

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

该引用参数是需要从表达式c.a = t t的 t 初始化的内容。 对于任何一个operator= ,引用都可以明确地绑定。

[dcl.init.ref]

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

  • 如果引用是左值引用和初始化表达式

    • [...]
    • 具有 class 类型(即 T2 是 class 类型),其中 T1 与 T2 没有引用相关,并且可以转换为“cv3 T3”类型的左值,其中“cv1 T1”与“cv3”引用兼容T3”(通过枚举适用的转换函数([over.match.ref])并通过重载决议选择最佳转换函数来选择此转换),

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

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

    • [...]
    • 具有 class 类型(即 T2 是 class 类型),其中 T1 与 T2 不引用相关,并且可以转换为右值或 ZC1C425268E68385D1AB5071 类型的“Tcv Tcv T3 引用兼容”,其中“lcv 值”为“Tcv TcvT93 引用兼容”使用“cv3 T3”(参见 [over.match.ref]),

    那么...第二种情况下的转换结果称为转换后的初始化程序。 如果转换后的初始值设定项是纯右值,则将其类型 T4 调整为类型“cv1 T4” ([conv.qual]) 并应用临时实现转换 ([conv.rval])。 在任何情况下,引用都绑定到生成的 glvalue(或适当的基础 class 子对象)。

关于为这两个案例构建候选集的主题,标准说

[over.match.ref]

1在 [dcl.init.ref] 中指定的条件下,引用可以直接绑定到将 function 转换为初始化表达式的结果。 重载解析用于select转换function被调用。 假设“reference to cv1 T”是被初始化的引用的类型,“cv S”是初始化表达式的类型,S为class类型,候选函数选择如下:

  • 考虑了 S 及其基类的转换函数。 那些不隐藏在 S 中并产生类型“对 cv2 T2 的左值引用”(初始化对函数的左值引用或右值引用时)或“cv2 T2”或“对 cv2 T2 的右值引用”(当初始化对函数的右值引用或左值引用),其中“cv1 T”与“cv2 T2”引用兼容,是候选函数。 对于直接初始化,那些不隐藏在 S 中并产生类型“对 cv2 T2 的左值引用”(当初始化对函数的左值引用或右值引用时)或“对 cv2 T2 的右值引用”(当初始化一个右值引用或对函数的左值引用),其中 T2 与 T 的类型相同,或者可以通过限定转换转换为 T 类型,也是候选函数。

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

  1. 对 T 的左值引用的初始化
    • 候选函数是那些产生“对 cv2 T2 的左值引用”的函数。
  2. 对 T 的右值引用的初始化
    • 候选函数是那些产生“cv2 T2”或“对 cv2 T2 的右值引用”的函数。

对于operator=(A const&) ,我们在案例 #1 中,并且有一个综合operator A const&()作为唯一的候选者。 对于operator=(A&&)它的情况 #2,非模板operator A()是唯一的候选者。 无论哪种方式,我们都有一个明确的隐式转换序列,其中包含一个用户定义的转换,它绑定了operator=的引用参数。

但是根据[over.match.best]中的规则,现在两个operator=都不是比另一个更可行的 function 。 根据[over.ics.rank]中的部分排序,这两种转换都不是更好。

这意味着由于对operator=的模棱两可的调用,该程序应该被声明为格式错误的。 但是,GCC 和 Clang 都出错(虽然不是 MSVC) 1 Clang 支持A&&过载,而 GCC 支持A const&并发出使用已删除转换 function 的诊断。 但这不是由于任何标准的强制行为。 理想情况下,他们都应该报告对operator=的调用不明确。


1 - 不同编译器行为的比较,带有简化的示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM