给定以下内容:
#include <string>
class s1
{
private:
std::string storage;
public:
s1(s1 *newValue) { storage=*newValue; }
operator std::string () { return storage; }
};
class s2
{
private:
std::string storage;
public:
s2(s2 *newValue) { std::string temp=*newValue; storage=temp; }
operator std::string const () { return storage; }
};
class s3
{
private:
std::string storage;
public:
s3(s3 *newValue) { storage=*newValue; } // Compile Error here.
operator std::string const () { return storage; }
};
第一个(s1)和第二个(s2)示例编译良好;第三个示例不能...但是s2和s3应该相同,除了通过temp变量传递值。
将错误消息追逐到库代码中,我认为编译器正在尝试绑定到 s3 情况下 std::string 上的移动构造函数。
我不明白的是它为什么要这样做;为什么我可以做我对 s2 所做的,但不能做 s3 ---它们不应该在语义上相同吗?
任何人可以提供的任何启发将不胜感激!
FWIW,IDE是Xcode Version 9.2(9C40b);C++变体是gnu++17。另外,这是来自Xcode的(略微编辑的)错误列表:
In file included from /Users/... big long path here... .cpp:9:
/Users/... big long path here... .hpp:77:31: error: no viable conversion from 's3' to 'std::__1::basic_string<char>'
s3(s3 *newValue) { storage=*newValue; }
^~~~~~~~~
In file included from /Users/... big long path here... .cpp:9:
In file included from /Users/... big long path here... .hpp:45:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:763:5: note: candidate constructor not viable: no known conversion from 's3' to 'const std::__1::basic_string<char> &' for 1st argument
basic_string(const basic_string& __str);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:768:5: note: candidate constructor not viable: no known conversion from 's3' to 'std::__1::basic_string<char> &&' for 1st argument
basic_string(basic_string&& __str)
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:778:31: note: candidate constructor not viable: no known conversion from 's3' to 'const value_type *' (aka 'const char *') for 1st argument
_LIBCPP_INLINE_VISIBILITY basic_string(const value_type* __s);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:810:5: note: candidate constructor not viable: no known conversion from 's3' to 'initializer_list<value_type>' (aka 'initializer_list<char>') for 1st argument
basic_string(initializer_list<value_type> __il);
^
In file included from /Users/... big long path here... .cpp:9:
/Users/... big long path here... .hpp:78:4: note: candidate function
operator std::string const () { return storage; }
^
In file included from /Users/... big long path here... .cpp:9:
In file included from /Users/... big long path here... .hpp:45:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:829:44: note: passing argument to parameter '__str' here
basic_string& operator=(basic_string&& __str)
^
1 error generated.
问题 CWG 1604 的解决方案使得使用可转换为const T
右值的初始值设定项直接初始化类类型T
的右值引用的格式不正确,但仍然可以形成隐式转换序列,因为分辨率不会更改隐式转换序列的相关部分。
因此,在您的s3
示例中,根据 [over.ics.rank]/3.2.3,在重载解析期间排名string::operator=(const string&)
和string::operator=(string&&)
:
S1 比标准转换序列 S2 更好的转换序列,如果
- S1 和 S2 是引用绑定,
。
两者都不引用在没有 ref 限定符的情况下声明的非静态成员函数的隐式对象参数,S1 将右值引用绑定到右值,S2 绑定左值引用
。
编译器最后选择string::operator=(string&&)
,因此在其参数的实际初始化发生时会导致错误。
在您的s2
示例中,语义是复制初始化而不是选择正确的赋值运算符,并且没有引用绑定。所以一切都很好。
感谢 T.C. 在本次讨论中指出这已经是 CWG 2077 的起草问题。
我会推测(但它只是你所看到的):
s2(s2 *newValue) { std::string temp=*newValue; storage=temp; }
可由编译器通过隐式强制转换自动完成。
其中:
s3(s3 *newValue) { storage=*newValue; }
不能。
括号中的第一个位置(*newValue),看看它是否误解了编译器中的意图。第二次尝试执行显式强制转换:
s3(s3 *newValue) { storage=((std::string)(*newValue)); }
如果这些都不起作用,我将不得不克服这是一个编译器问题。