用户定义的转换序列
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.
如其他答案所示,一旦您将复制初始化更改为直接初始化,所有编译器都会接受代码。
在我研究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 ofother
is notT
or derived fromT
[...], user-defined conversion sequences that can convert from the type ofother
toT
[...] 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.
如其他答案所示,一旦您将复制初始化更改为直接初始化,所有编译器都会接受代码。