当static_cast'ing为仅移动类型时,Clang vs. GCC

T. *_*out 5 c++ language-lawyer c++14 c++17

考虑以下简单的仅移动类:

struct bar {
    constexpr bar() = default;
    bar(bar const&) = delete;
    bar(bar&&)      = default;
    bar& operator=(bar const&) = delete;
    bar& operator=(bar&&)      = default;
};
Run Code Online (Sandbox Code Playgroud)

现在,让我们创建一个包装器:

template <class T>
struct box {
    constexpr box(T&& x)
        : _payload{std::move(x)}
    {}

    constexpr explicit operator T() &&
    {
        return std::move(_payload);
    }

  private:
    T _payload;
};
Run Code Online (Sandbox Code Playgroud)

并测试:

int main()
{
    auto x = box<bar>{bar{}};
    auto y = static_cast<bar&&>(std::move(x));
}
Run Code Online (Sandbox Code Playgroud)

如果使用-std=c++14或以上编译,Clang-6.0对此代码感到满意.但是,GCC-8.1会产生以下错误:

<source>: In function 'int main()':
<source>:29:45: error: invalid static_cast from type 'std::remove_reference<box<bar>&>::type' {aka 'box<bar>'} to type 'bar&&'
     auto y = static_cast<bar&&>(std::move(x));
Run Code Online (Sandbox Code Playgroud)

这是一个链接到编译器资源管理器来试用它.

所以我的问题是,谁是对的,谁是错的?

Vit*_*meo 5

简化示例:

struct bar 
{
    bar() = default;
    bar(bar const&) = delete;
    bar(bar&&) = default;
};

struct box 
{
    explicit operator bar() && { return bar{}; }
};

int main() { static_cast<bar&&>(box{}); }
Run Code Online (Sandbox Code Playgroud)

住在godbolt.org上


首先,让我们看看对于转换运算符来说意味着什么explicit:

转换函数可以是显式的,在这种情况下,它仅被视为用于直接初始化的用户定义的转换.否则,用户定义的转换不限于在分配和初始化中使用.

static_cast认为是直接初始化

表单中发生的初始化

T x(a);
T x{a};
Run Code Online (Sandbox Code Playgroud)

以及在新的表达式中,的static_cast表达式,功能表示法的类型转换,MEM-初始化器,和病症的支撑-INIT列表形式被称为直接初始化.


由于static_cast直接初始化,转换运算符是否标记无关紧要explicit.但是,删除explicit使代码在g ++clang ++上编译:在godbolt.org上的实例.

这让我相信这是一个g ++错误,因为explicit这里有所不同......当它不应该时.