重载解决方案:是否首选直接转换运算符(作为复制省略的结果)?
Overload-Resolution: Is a direct conversion operator preferred (as a consequence of copy-elision)?
给出
struct E
{
};
struct P
{
explicit P(E) {}
};
struct L
{
operator E() {return {};}
operator P() {return P{E{}};}
};
根据 C++17 语言标准,表达式 P{L{}}
应该编译吗?
不同的编译器产生不同的结果:
- gcc(主干):好的
- gcc 8.3: 错误(过载不明确)
- gcc 7.4:好的
- clang(中继):好的
- clang 8.0.0:好的
- clang 7.0.0:好的
- msvc v19.20:错误(过载不明确)
- icc 19.0.1: 错误(多个构造函数实例匹配)
我认为正确的行为符合标准是不明确的。
If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized.
Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution ([over.match], [over.match.list]). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
而[over.match.list]只是在谈论选择构造函数。我们有两个可行的选择:P(E)
通过 L{}.operator E()
和 P(P&&)
(隐式移动构造函数)通过 L{}.operator P()
。 None 另一个更好。
然而,这非常让人想起CWG 2327:
struct Cat {};
struct Dog { operator Cat(); };
Dog d;
Cat c(d);
问题表明当前调用 Cat(Cat&&)
而不仅仅是 d.operator Cat()
,这表明我们实际上也应该考虑转换函数。但这仍然是一个悬而未决的问题。我不确定 gcc 或 clang 做了什么来回应这个问题(或回应首先提出的类似例子),但根据你的结果我怀疑他们决定直接转换函数 L{}.operator P()
是更好的匹配,然后就这样做。
给出
struct E
{
};
struct P
{
explicit P(E) {}
};
struct L
{
operator E() {return {};}
operator P() {return P{E{}};}
};
根据 C++17 语言标准,表达式 P{L{}}
应该编译吗?
不同的编译器产生不同的结果:
- gcc(主干):好的
- gcc 8.3: 错误(过载不明确)
- gcc 7.4:好的
- clang(中继):好的
- clang 8.0.0:好的
- clang 7.0.0:好的
- msvc v19.20:错误(过载不明确)
- icc 19.0.1: 错误(多个构造函数实例匹配)
我认为正确的行为符合标准是不明确的。
If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized.
Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution ([over.match], [over.match.list]). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
而[over.match.list]只是在谈论选择构造函数。我们有两个可行的选择:P(E)
通过 L{}.operator E()
和 P(P&&)
(隐式移动构造函数)通过 L{}.operator P()
。 None 另一个更好。
然而,这非常让人想起CWG 2327:
struct Cat {}; struct Dog { operator Cat(); }; Dog d; Cat c(d);
问题表明当前调用 Cat(Cat&&)
而不仅仅是 d.operator Cat()
,这表明我们实际上也应该考虑转换函数。但这仍然是一个悬而未决的问题。我不确定 gcc 或 clang 做了什么来回应这个问题(或回应首先提出的类似例子),但根据你的结果我怀疑他们决定直接转换函数 L{}.operator P()
是更好的匹配,然后就这样做。