英文:
Supposedly ambiguous explicit conversion operator in MSVC, not in gcc or clang
问题
以下是代码部分的中文翻译:
class String {
public:
String(std::string_view s) : str{s} {}
operator std::string() const {
return str;
}
operator const char*() const {
return str.c_str();
}
private:
std::string str;
};
int main()
{
static_assert(std::is_convertible_v<String, std::string>);
String s("Hello World");
auto x = static_cast<std::string>(s);
return 0;
}
MSVC 告诉我,对 std::string
的静态转换是不明确的,而 clang 和 gcc 没有这个问题:https://godbolt.org/z/7de5YvTno
<source>(20): error C2440: 'static_cast': 无法从 'String' 转换为 'std::string'
<source>(20): note: 源类型可能不存在构造函数,或者构造函数重载存在歧义
Compiler returned: 2
哪个编译器是正确的?
作为一个后续问题:我可以通过将转换操作标记为显式来解决这个歧义。但是然后 std::is_convertible_v<String, std::string>
返回 false
。是否有一个类型特性 is_explicitly_convertible
或 is_static_castible
或类似的特性?
PS:我知道最简单且最干净的解决方法是有一个非复制的转换操作到 const std::string&
,但我仍然想了解为什么 MSVC 拒绝了这段代码。
英文:
Consider the following class that implements a user-conversion to std::string
and const char*
:
class String {
public:
String(std::string_view s) : str{s} {}
operator std::string() const {
return str;
}
operator const char*() const {
return str.c_str();
}
private:
std::string str;
};
int main()
{
static_assert(std::is_convertible_v<String, std::string>);
String s("Hello World");
auto x = static_cast<std::string>(s);
return 0;
}
MSVC tells me that static_casting to std::string
is ambiguous, while clang and gcc do not: https://godbolt.org/z/7de5YvTno
<source>(20): error C2440: 'static_cast': cannot convert from 'String' to 'std::string'
<source>(20): note: No constructor could take the source type, or constructor overload resolution was ambiguous
Compiler returned: 2
Which compiler is right?
As a follow-up question: I can fix the ambiguity by marking the conversion operations explicit. But then std::is_convertible_v<String, std::string>
returns false
. Is there a type trait is_explicitly_convertible
or is_static_castible
or similar?
PS: I know the simplest and cleanest solution would be to have a non-copying conversion operator to const std::string&
, but I still want to understand why MSVC rejects the code.
答案1
得分: 3
这是CWG2327。
按照书写,static_cast
指定的直接初始化严格考虑了 std::string
的构造函数,而 String
参数需要无法比较的用户定义转换来满足 const char*
的移动构造函数或转换构造函数。需要注意的是,无论可用的转换函数集是什么,都必须调用构造函数,这就是该问题中提到的拷贝省略遗漏的地方。
意图是同时考虑x
的实际初始化的构造函数和转换函数。这在某种程度上有点不同寻常,因为std::string
和String
的成员函数在同一个重载集中,但这解决了歧义,因为调用operator std::string
对于(隐含的对象)参数来说是一个完全匹配,并且它允许x
成为常规C++17意义上该函数的返回值。
MSVC正在按照标准的规定实现,而GCC和Clang正在实现(类似于)预期的解决方案。
(std::is_constructible
大致上是你也提到的直接初始化特性。)
英文:
This is CWG2327.
As written, the direct-initialization specified for static_cast
strictly considers constructors for std::string
, and the String
argument requires incomparable user-defined conversions to satisfy either the move constructor or the converting constructor from const char*
. Note that, regardless of the set of conversion functions available, a constructor must be called, which is the missed copy elision mentioned in the issue.
The intent is that constructors and conversion functions are simultaneously considered for the actual initialization of x
. This is a bit unusual in that member functions of std::string
and String
are in the same overload set, but it resolves the ambiguity because calling operator std::string
is an exact match for the (implied object) argument, and it allows x
to be the return value from that function in the ordinary C++17 sense.
MSVC is implementing the standard as written, while GCC and Clang are implementing (something like) the intended resolution.
(std::is_constructible
is more or less the direct-initialization trait you also asked about.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论