派生 class 类型到基 class 类型是否包含在用户定义的转换中?

Is derived class type to base class type subsumed by user-defined conversion?

自从阅读 cplusplus 标准的一些引述后,我对从派生 class 类型转换基本 class 类型感到困惑。该转化是否属于用户自定义转化?

引用一些让我对此感到困惑的引述:

[class.conv]/1 Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions...

[class.conv.ctor]/3
A non-explicit copy/move constructor ([class.copy]) is a converting constructor.

#include <iostream>
struct Base{
   Base() = default;
   Base(Base const&){}
};
struct Derived:Base{
};
int main(){
   Derived d;
   Base b = d; //from d to b, is this a user-defined conversion? Before reading the standard, I think it's not, but now I'm confused about this.
}

所以根据这些引述,派生class类型对象到基础class类型属于用户定义的转换。如果我遗漏了标准中写的 Derived class type to Base class type would not belong to user-defined conversion,请纠正我。

此处不考虑用户定义的转换。该标准列出了复制初始化的两种情况(其中语法 Base b = d; 是一种形式)。他们是

[dcl.init]/17.16.2

Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. ...

[dcl.init]/17.16.3

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversions that can convert from the source type to the destination type ... [are used].

这种情况,因为 Derived 派生自 Base,所以使用前一个子句而不是后者。因此,仅参考 Base 的构造函数,而不是例如可以在 Derived.

中定义的任何 operator Base()

好的,让我们分解一下标准的内容:

Type conversions of class objects can be specified by constructors and by conversion functions.

现在,让我们假装我们对这些词的含义一无所知。这句话讲的是一个概念,叫"type conversions",但具体讲的是"type conversions of class objects"。所以我们不是在谈论所有类型转换,只是其中的一个子集。

然后它说 "can be specified",它列出了几种可以指定它们的方法。下一句:

These conversions are called user-defined conversions

请注意,它没有说 "these constructors" 或 "these conversion functions"。它说 "these conversions." 好吧,唯一讨论过的 "conversions" 是之前讨论过的子集:"type conversions of class objects"。因此,这句话可以重述为:

[type conversions of class objects] are called user-defined conversions.

因此,我们可以从中看出 class 对象可以进行类型转换。这些转换可以由 class 上的某些内容指定。这种特殊类型的转换称为 "user-defined conversions".

标准从来没有说构造函数本身是类型转换或用户定义的转换。构造函数只是指定此类转换的一种方式。

接下来,我们继续 [class.conv.ctor]/1:

A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters (if any) to the type of its class. Such a constructor is called a converting constructor.

好的,我们现在有了 "converting constructor" 的定义。实际上,鉴于此定义,第 3 段(声明非 explicit copy/move 构造函数正在转换构造函数)是多余的;上面的定义清楚地表明它们是。

作为一个"converting constructor"是一个属性的构造函数。一个user-defined conversion的过程拼出来了,肯定可以调用一个"converting constructor"。但在任何时候都没有声明或暗示这是可以调用 "converting constructor" 的 唯一 过程。

因此,复制构造函数是一个 "converting constructor" 这一事实不应被解释为任何导致调用复制构造函数的结果本身就是用户定义的转换。当标准它们发生时,用户定义的转换就会发生。

在您描述的示例中,发生的情况在 [dcl.init]/17.6.2:

中定义

Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution. The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

这条规则本身没有任何地方声明直接执行任何类型的转换。发生的是对目标类型的单参数构造函数的重载决策。重载决议规则可以考虑许多转换,因为它试图使给定参数适合重载集中的各种参数可能性。但那些是通用的,与任何函数调用的任何重载决议相关联。

也就是说,恰好选择的函数恰好被视为 "converting constructor" 并不意味着用户定义的转换导致它被调用。