17
class C {
public:
    C() { }
};

class B {
public:
    B(C c) { }
    B() { }
};

class A {
public:
    A(bool b) { }
    A(B b) { }
};

int main() {
    A a1 = true; // bool -> A        is allowed
    A a2 = B();  // B -> A           is allowed

    A a3 = 7;    // int -> bool -> A is allowed
    A a4 = C();  // C -> B -> A      isn't allowed
}

为什么我可以使用两步隐式转换bool但不能使用它C?描述多步隐式转换的一般规则是什么?

4

2 回答 2

15

没有多步用户定义的隐式转换。

int -> bool -> A

是允许的,因为int->bool转换不是用户定义的。

12.3 转换 [class.conv]

1 类对象的类型转换可以由构造函数和转换函数指定。这些转换称为用户定义的转换,用于隐式类型转换(第 4 条)、初始化(8.5)和显式类型转换(5.4、5.2.9)。

2 用户定义的转换仅适用于明确的情况(10.2、12.3.2)。转换遵守访问控制规则(条款 11)。在歧义解决(3.4)之后应用访问控制。

3 [注:有关在函数调用中使用转换的讨论以及下面的示例,请参见 13.3。——尾注]

4 最多一个用户定义的转换(构造函数或转换函数)隐式应用于单个值。

于 2012-10-11T19:44:28.310 回答
1

由于这种结构是完全合法的

A a4((C()));

问题是,您使用复制初始化。真的,你的例子等于

A a4((A(C()));

8.5/16

初始化器的语义如下。目标类型是正在初始化的对象或引用的类型,源类型是初始化表达式的类型。如果初始化器不是单个(可能是带括号的)表达式,则未定义源类型。

如果目标类型是(可能是 cv 限定的)类类型:

— 否则(即,对于剩余的复制初始化情况),可以从源类型转换到目标类型或(当使用转换函数时)到其派生类的用户定义转换序列被枚举,如 13.3 中所述.1.4,并且通过重载决议(13.3)选择最好的一个。

13.3.1.4/1

在 8.5 中指定的条件下,作为类类型对象的复制初始化的一部分,可以调用用户定义的转换来将初始化表达式转换为正在初始化的对象的类型。

重载解析用于选择要调用的用户定义转换。假设“cv1 T”是被初始化对象的类型,T是类类型,候选函数的选择如下: — T的转换构造函数(12.3.1)是候选函数。

— 当初始化表达式的类型是类类型“cv S”时,考虑 S 及其基类的非显式转换函数。

13.3.3.1/4

但是,当考虑构造函数或用户定义转换函数的参数时,当在类复制初始化的第二步中被调用以复制/移动临时对象时,13.3.1.3 为候选者,当传递时由 13.3.1.7初始化器列表作为单个参数,或者当初始化器列表只有一个元素并且对某个类 X 的转换或对(可能是 cv 限定的)X 的引用被认为是 X 的构造函数的第一个参数时,或由13.3.1.4、13.3.1.5 或 13.3.1.6 在所有情况下,仅考虑标准转换序列和省略号转换序列。

在这种情况下,不考虑您的用户定义转换 (C -> B)。

于 2012-10-11T21:08:04.600 回答