显式转换运算符和常量引用限定
explicit conversion operator and const-reference qualification
以下示例在 godbolt 上使用 -std=c++17
和 clang 编译正常,但在 msvc 和 gcc 上编译失败:
struct Foo
{
};
struct Bar
{
explicit operator Foo() // w/o explicit qualification all are happy
{
return Foo();
}
};
int main()
{
Bar b;
const Foo& foo = static_cast<const Foo&>(b); // only clang happy with this
const Foo& foo2 = static_cast<const Foo&>(static_cast<Foo>(b)); // clang / msvc / gcc happy with this
return 0;
}
据我所知,运算符上的 explicit
只是禁止 implicit conversions,并且由于该页面列出了它们之间的限定转换,我认为在这种情况下 clang 完全是错误的?还是我遗漏了什么?
理想情况下,我想坚持使用单一转换,因为我在模板代码中使用它,如果常量引用转换运算符可用,它就会被使用。但我也可以放弃 explicit
约束。
OP 的示例并不简单。一个真正最小的例子使用,而不是 static_cast
然后初始化,简单地:
const Foo& foo(b);
无论如何,这就是 static_cast<const Foo&>(b)
应该做的。
在这里,Clang 再次接受而 GCC 拒绝 [godbolt]。如果将其更改为复制初始化 (const Foo& foo = b;
),则 Clang 和 GCC 都会拒绝。
看来Clang是对的:在直接初始化的情况下,为什么不用explicit
转换函数呢?
问题被标记为 c++11,所以请参阅 C++11 [over.match.ref]/1:
Under the conditions specified in 8.5.3, a reference can be bound directly to a glvalue or class prvalue that is the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1 T
” is the underlying type of the reference being initialized, and “cv S
” is the type of the initializer expression, with S
a class type, the candidate functions are selected as follows:
- The conversion functions of
S
and its base classes are considered, except that for copy-initialization, only the non-explicit conversion functions are considered. Those that are not hidden within S
and yield type “lvalue reference to cv2 T2
” (when 8.5.3 requires an lvalue result) or “cv2 T2
” or “rvalue reference to cv2 T2
” (when 8.5.3 requires an rvalue result), where “cv1 T
” is reference-compatible (8.5.3) with “cv2 T2
”, are candidate functions.
8.5.3 中的措辞是 defect report 的主题,已在 C++14 中解决,以阐明引用绑定不使用“隐式转换”,而是使用“转换” (其中 [over.match.ref] 仅在复制初始化情况下排除 explicit
函数)。该决议应被视为追溯至 C++11。
每个版本的标准措辞都有所不同,但我认为结论是一样的。
我查找了关于此问题的现有 GCC 错误报告,但令人惊讶的是找不到。我认为打开错误报告是个好主意,如果 GCC 开发人员坚持认为 GCC 是正确的,他们会解释为什么他们这么认为。
以下示例在 godbolt 上使用 -std=c++17
和 clang 编译正常,但在 msvc 和 gcc 上编译失败:
struct Foo
{
};
struct Bar
{
explicit operator Foo() // w/o explicit qualification all are happy
{
return Foo();
}
};
int main()
{
Bar b;
const Foo& foo = static_cast<const Foo&>(b); // only clang happy with this
const Foo& foo2 = static_cast<const Foo&>(static_cast<Foo>(b)); // clang / msvc / gcc happy with this
return 0;
}
据我所知,运算符上的 explicit
只是禁止 implicit conversions,并且由于该页面列出了它们之间的限定转换,我认为在这种情况下 clang 完全是错误的?还是我遗漏了什么?
理想情况下,我想坚持使用单一转换,因为我在模板代码中使用它,如果常量引用转换运算符可用,它就会被使用。但我也可以放弃 explicit
约束。
OP 的示例并不简单。一个真正最小的例子使用,而不是 static_cast
然后初始化,简单地:
const Foo& foo(b);
无论如何,这就是 static_cast<const Foo&>(b)
应该做的。
在这里,Clang 再次接受而 GCC 拒绝 [godbolt]。如果将其更改为复制初始化 (const Foo& foo = b;
),则 Clang 和 GCC 都会拒绝。
看来Clang是对的:在直接初始化的情况下,为什么不用explicit
转换函数呢?
问题被标记为 c++11,所以请参阅 C++11 [over.match.ref]/1:
Under the conditions specified in 8.5.3, a reference can be bound directly to a glvalue or class prvalue that is the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1
T
” is the underlying type of the reference being initialized, and “cvS
” is the type of the initializer expression, withS
a class type, the candidate functions are selected as follows:
- The conversion functions of
S
and its base classes are considered, except that for copy-initialization, only the non-explicit conversion functions are considered. Those that are not hidden withinS
and yield type “lvalue reference to cv2T2
” (when 8.5.3 requires an lvalue result) or “cv2T2
” or “rvalue reference to cv2T2
” (when 8.5.3 requires an rvalue result), where “cv1T
” is reference-compatible (8.5.3) with “cv2T2
”, are candidate functions.
8.5.3 中的措辞是 defect report 的主题,已在 C++14 中解决,以阐明引用绑定不使用“隐式转换”,而是使用“转换” (其中 [over.match.ref] 仅在复制初始化情况下排除 explicit
函数)。该决议应被视为追溯至 C++11。
每个版本的标准措辞都有所不同,但我认为结论是一样的。
我查找了关于此问题的现有 GCC 错误报告,但令人惊讶的是找不到。我认为打开错误报告是个好主意,如果 GCC 开发人员坚持认为 GCC 是正确的,他们会解释为什么他们这么认为。