显式转换运算符和常量引用限定

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 是正确的,他们会解释为什么他们这么认为。