由于在 C++ 中有两种定义转换的方法,当同一转换有两种可能性时,它们如何交互?

2023-12-23

我只是想了解 C++ 的工作原理,这并不是要解决我的代码中的特定问题。

在 C++ 中,您可以说类型 A 应该以两种不同的方式隐式转换为类型 B。

如果您是 A 的作者,您可以向 A 添加如下内容:

operator B() {
   // code
}

如果您是 B 的作者,您可以向 B 添加如下内容:

B(const A &a) {
    // code
}

如果我理解正确的话,其中任何一个都将允许 A 隐式转换为 B。那么,如果两者都定义了,则使用哪一个?这还被允许吗?

注意:我知道您可能永远不应该处于这样做的情况。您可以使构造函数显式化,或者更有可能只有两者之一。我只是想知道 C++ 规范的内容,但我不知道如何查找。


不幸的是,这个问题的答案可能比您想要的更复杂。正如 Orbit 中的 Lightness Races 指出的那样,编译器确实会拒绝不明确的转换,但是这些转换是否不明确?让我们看几个案例。所有引用均参考 C++11 标准。

显式转换

这并没有直接解决您的问题,因为您询问了隐式转换,但由于轨道中的 Lightness Races 给出了显式转换的示例,因此我无论如何都会讨论它。

显式转换是从A to B when:

  • 你使用语法(B)a, where a属于类型A,在这种情况下相当于static_cast<B>(a)(C++11 标准,§5.4/4)。
  • 您使用静态强制转换,在本例中将创建一个临时变量,该临时变量的初始化方式与声明的方式相同B t(a);初始化t; (§5.2.9/4)
  • 你使用语法B(a),这相当于(B)a因此也做了与声明中的初始化相同的事情B t(a);(§5.2.3/1)

因此,在每种情况下,都会对类型的纯右值执行直接初始化B使用类型值A作为论点。 §8.5/16 规定仅考虑构造函数, so B::B(const A&)将被调用。 (更详细的信息,请参阅我的回答:https://stackoverflow.com/a/22444974/481267 https://stackoverflow.com/a/22444974/481267)

复制初始化

在复制初始化中

B b = a;

价值a类型的A首先转换为临时类型B使用用户定义的转换序列,这是一个隐式转换序列。然后这个临时用于直接初始化b.

因为这是由不同类类型的对象对类类型进行复制初始化,both转换构造函数B::B(const A&)和转换函数A::operator B()是转换的候选者(§13.3.1.4)。之所以称其为后者,是因为它赢得过载决议。请注意,如果B::B有争论A&而不是const A&,重载将不明确并且程序无法编译。有关标准的详细信息和参考,请参阅此答案:https://stackoverflow.com/a/1384044/481267 https://stackoverflow.com/a/1384044/481267

复制列表初始化

复制列表初始化

B b = {a};

只考虑构造函数B(§8.5.4/3),而不是转换函数A, so B::B(const A&)将被调用,就像显式转换一样。

函数参数的隐式转换

如果我们有

void f(B b);
A a;
f(a);

那么编译器必须选择最佳的隐式转换序列进行转换a输入B为了将它传递给f。为此,考虑用户定义的转换序列,其中包含一个标准转换,后跟一个用户定义的转换,再后跟另一个标准转换(§13.3.3.1.2/1)。用户定义的转换可以通过转换构造函数进行B::B(const A&)或者通过转换函数A::operator B().

这就是事情变得棘手的地方。标准中有一些令人困惑的措辞:

由于隐式转换序列是初始化,因此初始化的特殊规则 按用户定义的转换 在为用户定义的转换选择最佳的用户定义的转换时适用 顺序(见 13.3.3 和 13.3.3.1)。

(§13.3.3.1.2/2)

长话短说,这意味着用户定义的转换序列中的用户定义的转换A to B本身受到重载解析的影响;A::operator B()战胜B::B(const A&)因为前者的简历资格较少(如在复制初始化的情况下),如果我们有的话,就会导致歧义B::B(A&)而不是B::B(const A&)。请注意,这不会导致重载解析的无限递归,因为不允许用户定义的转换将参数转换为用户定义的转换的参数类型。

退货声明

In

B foo() {
    return A();
}

表达方式A()隐式转换为类型B(§6.6.3/2) 因此,应用与函数参数的隐式转换相同的规则;A::operator B()将被调用,如果我们有的话,重载将是不明确的B::B(A&)。然而,如果换做是

return {A()};

那么这将是一个复制列表初始化(再次§6.6.3/2);B::B(const A&)将被调用。

Note:处理异常时不会尝试用户定义的转换; Acatch(B)块不会处理throw A();.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

由于在 C++ 中有两种定义转换的方法,当同一转换有两种可能性时,它们如何交互? 的相关文章

随机推荐