用户定义的转换序列

User-defined conversions sequence

在我研究explicit关键字之前,我的老师说:"compiler doesn't execute consecutive user defined conversion"。如果是真的,我的代码中是否有任何错误?还是我误解了我的老师?我在 VS2017 工作。

#include<iostream>
#include <string>

class Myclass {
public:
    Myclass() {
        std::cout << "Myclass" << std::endl;
    }
};

class Myclass1 {
public:
    Myclass1(Myclass m) {
        std::cout << "Myclass1" << std::endl;
    }
};
class Myclass2{
public:
    Myclass2(Myclass1 m) {
        std::cout << "Myclass2" << std::endl;
    }
};

int main() {
    Myclass2 m2 = Myclass{};
} 

compiler doesn't execute consecutive user defined conversion

你的老师是对的。在您的代码示例中,这意味着当您分配 in:

时,Myclass 无法转换为 Myclass1
Myclass2 m2 = Myclass{};

因为构造函数在创建 Myclass2 时期望 Myclass1,而编译器无法连续将 Myclass 转换为 Myclass1,然后将其用于创建 Myclass2。但是,如果您有以下行:

Myclass1 m2 = Myclass{};

它会起作用,因为 Myclass1 的构造函数将 Myclass 作为参数。

更新:

您可能会问为什么这样做:

Myclass2 m2 {Myclass{}};

因为在这种情况下,构造函数被调用并且转换可以隐式完成,除非您将 Myclass1 声明为 explicit 这将导致代码编译失败(感谢 Fureeish 提醒),但是在:

Myclass2 m2 = Myclass{};

就像调用需要引用的拷贝构造函数。所以如果你这样写,它会起作用:

Myclass2 m2 = Myclass1(Myclass{});

EVG所述,如果未激活一致性模式(/permissive-),VS 2017 会接受Myclass2 m2 = Myclass{};

 Myclass2 m2 = Myclass{};

表示复制初始化。引用 cppreference.com:

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T [...], user-defined conversion sequences that can convert from the type of other to T [...] are examined and the best one is selected through overload resolution.

引用 further:

A user-defined conversion consists of zero or one non-explicit single-argument constructor or non-explicit conversion function call.

因此,Myclass2 m2 = Myclass{}; 是不可接受的,因为它会涉及两个用户定义的转换。


现在让我们来看看

Myclass2 m2 {Myclass{}};

Afshin 的回答中提出。这是一个直接初始化。规则是 different:

The constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.

Myclass2 的构造函数接受 Myclass1,您需要一个用户定义的转换才能从 Myclass 得到 Myclass1。因此,它编译。


注意在 VS 中 copy-initilization 被视为 direct-initilization 如果一致性模式 (/premissive-) 不是激活(默认)。因此,VS 接受 Myclass2 m2 = Myclass{}; 将其视为 直接初始化 。有关示例,请参阅 this document

其他答案都在埋头苦干:你写的代码确实无效。 MSVC 默认接受它,但 MSVC 这样做是错误的。您可以使用命令行开关 /permissive- 强制 MSVC 更严格。 (你应该使用那个开关。)

Other compilers (GCC, clang), reject it.

如其他答案所示,一旦您将复制初始化更改为直接初始化,所有编译器都会接受代码。