gpt4 book ai didi

c++ - 哪些规则控制在类型之间使用多个用户定义的转换?

转载 作者:行者123 更新时间:2023-12-03 18:28:58 25 4
gpt4 key购买 nike

我有这个代码:

class MyString
{
public:
operator const char*() const {
return nullptr;
}
};

class YourString
{
public:
YourString() {}
YourString(const char* ptr) {
(void)ptr;
}

YourString& operator=(const char* ptr)
{
return *this;
}
};

int main()
{
MyString mys;

YourString yoursWorks;
yoursWorks = mys;

YourString yoursAlsoWorks(mys);

YourString yoursBreaks = mys;
}

MSVC 毫无问题地接受它。 Clang-CL 不接受:
$ "C:\Program Files\LLVM\msbuild-bin\CL.exe" ..\string_conversion.cpp
..\string_conversion.cpp(32,13): error: no viable conversion from 'MyString' to 'YourString'
YourString yoursBreaks = mys;
^ ~~~
..\string_conversion.cpp(10,7): note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'MyString' to
'const YourString &' for 1st argument
class YourString
^
..\string_conversion.cpp(10,7): note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'MyString' to
'YourString &&' for 1st argument
class YourString
^
..\string_conversion.cpp(14,2): note: candidate constructor not viable: no known conversion from 'MyString' to 'const char *' for 1st argument
YourString(const char* ptr) {
^
..\string_conversion.cpp(5,2): note: candidate function
operator const char*() const {
^
1 error generated.

海湾合作委员会也没有:
$ g++.exe -std=gnu++14 ..\string_conversion.cpp
..\string_conversion.cpp: In function 'int main()':
..\string_conversion.cpp:33:27: error: conversion from 'MyString' to non-scalar type 'YourString' requested
YourString yoursBreaks = mys;
^

我了解只允许一种用户定义的转换。

但是,MSVC 是否有理由处理该线路
YourString yoursBreaks = mys;

作为
YourString yoursBreaks(mys);

并接受它?那是不是允许编译器做一个转换?在什么规则下允许/不允许?有没有类似的规定?

更新:使用 MSVC, /Za flag 导致代码不被接受。
$ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64\CL.exe" /Za ..\string_conversion.cpp

string_conversion.cpp
..\string_conversion.cpp(33): error C2440: 'initializing': cannot convert from 'MyString' to 'YourString'
..\string_conversion.cpp(33): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

最佳答案

tldr;代码格式错误,MSVC 错误地接受它。复制初始化不同于直接初始化。外行的解释是yoursBreaks的初始化将涉及两次用户定义的转换( MyString --> const char* --> YourString ),而直接初始化涉及一次用户定义的转换( MyString --> const char* ),并且您最多只能进行一次用户定义的转换。强制执行该规则的标准解释是 [over.best.ics] 不允许在通过转换构造函数从不相关的类类型复制初始化类类型的上下文中进行用户定义的转换。

达到标准!有什么作用:

YourString yoursBreaks = mys;

意思?任何时候我们声明一个变量,都是某种初始化。在这种情况下,根据 [dcl.init]:

The initialization that occurs in the = form of a brace-or-equal-initializer or condition (6.4), as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.6.1), is called copy-initialization.



复制初始化是任何形式的 T var = expr;尽管出现了 = ,这从不调用 operator= .我们总是通过构造函数或转换函数。

具体来说,这个案例:

If the destination type is a (possibly cv-qualified) class type:
— If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, [...]
— 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, [...]
— Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed.



我们陷入了最后的子弹。让我们跳到 13.3.1.4:

— The converting constructors (12.3.1) of T are candidate functions.
— When the type of the initializer expression is a class type “cv S”, the non-explicit conversion functions of S and its base classes are considered. When initializing a temporary to be bound to the first parameter of a constructor where the parameter is of type “reference to possibly cv-qualified T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T”, explicit conversion functions are also considered. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions. Conversion functions that return “reference to X” return lvalues or xvalues, depending on the type of reference, of type X and are therefore considered to yield X for this process of selecting candidate functions.



第一个要点为我们提供了 YourString 的转换构造函数, 哪个是:
YourString(const char* );

第二颗子弹什么也没给我们。 MyString没有返回 YourString 的转换函数或从它派生的类类型。

所以,好吧。我们有一个候选构造函数。它可行吗? [over.match] 通过以下方式检查可靠性:

Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.



并且,在 [over.best.ics] 中:

A well-formed implicit conversion sequence is one of the following forms:
— a standard conversion sequence (13.3.3.1.1),
— a user-defined conversion sequence (13.3.3.1.2), or
— an ellipsis conversion sequence (13.3.3.1.3).

However, if the target is
the first parameter of a constructor or
— the implicit object parameter of a user-defined conversion function

and the constructor or user-defined conversion function is a candidate by
— 13.3.1.3, when the argument is the temporary in the second step of a class copy-initialization,
13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases), or
— the second phase of 13.3.1.7 [...]
user-defined conversion sequences are not considered. [ Note: These rules prevent more than one user-defined conversion from being applied during overload resolution, thereby avoiding infinite recursion. —end note ] [ Example:

struct Y { Y(int); };
struct A { operator int(); };
Y y1 = A(); // error: A::operator int() is not a candidate

struct X { };
struct B { operator X(); };
B b;
X x({b}); // error: B::operator X() is not a candidate

—end example ]



所以即使有来自 MyString 的转换序列至 const char* ,在这种情况下不考虑,所以这个构造函数是不可行的。

由于我们没有另一个候选构造函数,因此调用格式错误。

另一行:
YourString yoursAlsoWorks(mys);

称为直接初始化。我们调用我之前引用的 [dcl.init] 块中的三个要点中的第二个要点,它的整体内容如下:

The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). 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.



其中 13.3.1.3 表示构造函数是从以下位置枚举的:

For direct-initialization or default-initialization that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized.



这些构造函数是:
YourString(const char* )        // yours
YourString(YourString const& ) // implicit
YourString(YourString&& ) // implicit

为了检查后两个函数的可行性,我们从复制初始化上下文(如上述失败)重新执行重载解析。但是对于您的 YourString(const char*) ,很简单, MyString 有一个可行的转换函数至 const char* ,所以它被使用了。

请注意,这里有一个单一的转换: MyString --> const char* .一次转换没问题。

关于c++ - 哪些规则控制在类型之间使用多个用户定义的转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41594186/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com