【问题标题】:What are the properties of template conversion `operator const T &` in C++?C++中模板转换`operator const T &`的属性是什么?
【发布时间】:2021-11-06 11:18:52
【问题描述】:

每个 C++ 类都可以声明其他类型的转换运算符,特别是运算符可以是模板,并且可以转换为 const 引用类型。但看起来现代编译器对这个运算符的处理方式有所不同。下一个例子:

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

int main() { 
    A a;
    [[maybe_unused]] B b(a); //error in Clang
    [[maybe_unused]] int c(a); //error in GCC
}

被 MSVC 完全接受。

GCC 接受从 AB 的转换,尽管我希望首选的非模板 A::operator B 已被明确删除。

同时GCC拒绝从Aint的转换:

cannot convert 'A' to 'int' in initialization

演示:https://gcc.godbolt.org/z/WvafjPPjr

我个人更喜欢这里 Clang 的行为,但它真的是对的吗?

【问题讨论】:

  • 也许是一个有用的细节:operator B const&amp; () = delete; 将导致all three compilers to reject B 转换。我认为 MSVC 和 Clang 正在尝试使用模板函数进行转换。
  • B b(a) 可能与Bug 77660 相关,而int c(a)Bug 63217 相关。

标签: c++ templates type-conversion language-lawyer


【解决方案1】:

B b(a); 是类类型的直接初始化。根据[dcl.init.general]B 的构造函数在重载决议中被考虑。

B 具有三个隐式生成的构造函数:

B();         // #1
B(const B&); // #2
B(B&&);      // #3

根据[over.match.viable],只有#2 和#3 是可行的,因为#1 没有足够的参数。

重载解析尝试形成从a到每个构造函数的参数类型的隐式转换序列。

根据[conv.general],转换为引用等同于引用初始化。

根据[dcl.init.ref],在const B&amp;的初始化中,如果源表达式可以通过operator const B&amp;转换为const B类型的左值,则引用绑定到转换结果。

再次根据[dcl.init.ref],在B&amp;&amp; 的初始化中,如果源表达式可以通过operator B 转换为B 类型的右值,则引用绑定到转换结果。否则,同样根据[dcl.init.ref],考虑其他用户定义的转换。

由于a不能以任何方式转换为B&amp;&amp;,可以转换为const B&amp;,所以#2获胜。所以a被转换成const B&amp;,然后通过拷贝构造函数用来初始化b

int c(a); 是非类类型的直接初始化。根据[dcl.init.general],在重载解析中考虑了A 的转换函数。

根据[temp.deduct.conv]operator const T &amp;的模板参数可以推导出为int

由于operator const int&amp;的结果可以转换为int,所以初始化是良构的。

所以我相信这两个初始化都是有效的。也就是说,MSVC 是对的。但是,[dcl.init.ref] 中的“可以转换”措辞很草率,可能意味着理论上可以通过已删除的转换函数转换值,在这种情况下 Clang 可能是正确的。

【讨论】:

  • 谢谢。而在反对的情况下,当operator const B &amp; 被删除但模板operator T 存在时:gcc.godbolt.org/z/Pxoe51Mav。现在是 Clang 而不是 MSVC 吗?
猜你喜欢
  • 2019-09-14
  • 2017-10-07
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 1970-01-01
  • 2011-01-18
  • 2012-03-29
  • 1970-01-01
相关资源
最近更新 更多