我正在尝试使用clang编译WebKit,并且由于本质上是以下模式,因此我命中了compile errors:
#include <iostream>
#include <optional>
struct X {
X() = default;
X(const X& other) { }
};
struct Y {
std::optional<X> x;;
};
int main() {
Y foo;
Y bar(std::move(foo));
}
因此,他们使用std::optional<T>
,其中T(在他们的情况下为WTF::Variant
)具有非平凡的复制/移动构造函数,然后使用std::optional
移动构造函数。使用GCC 8.1.1可以很好地进行编译,但是不能使用clang 6.0.1(使用GCC 8.1.1的libstdc ++)进行编译:
In file included from test.cpp:2:
/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.1.1/../../../../include/c++/8.1.1/optional:276:9: error: call to implicitly-deleted copy constructor of 'std::_Optional_payload<X, true, true, true>'
: _Optional_payload(__engaged
^ ~~~~~~~~~
/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.1.1/../../../../include/c++/8.1.1/optional:739:4: note: in instantiation of member function 'std::_Optional_payload<X, true, true, true>::_Optional_payload' requested here
: _M_payload(__other._M_payload._M_engaged,
^
/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.1.1/../../../../include/c++/8.1.1/optional:985:11: note: in instantiation of member function 'std::_Optional_base<X, false, false>::_Optional_base' requested here
class optional
^
test.cpp:9:8: note: in implicit move constructor for 'std::optional<X>' first required here
struct Y {
^
test.cpp:15:7: note: in implicit move constructor for 'Y' first required here
Y bar(std::move(foo));
^
/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.1.1/../../../../include/c++/8.1.1/optional:288:24: note: copy constructor of '_Optional_payload<X, true, true, true>' is implicitly deleted because variant field '_M_payload' has a
non-trivial copy constructor
_Stored_type _M_payload;
这是有效的C ++,还是WebKit损坏并且clang正确拒绝了此代码?
答案 0 :(得分:6)
考虑此类:
struct X
{
X(int);
X(X&&) = delete;
// does this need to invoke the move constructor??
X() : X(X(0)) { }
};
根据gcc,答案是否定的:这直接委托给X(int)
。根据clang的说法,答案是肯定的,并且编译失败:
<source>:55:15: error: call to deleted constructor of 'X' X() : X(X(0)) { } ^ ~~~~ <source>:52:9: note: 'X' has been explicitly marked deleted here X(X&&) = delete; ^
这似乎是解决核心语言问题的潜力,因为一方面[class.base.init]/6说:
通过重载分辨率选择目标构造函数。目标构造函数返回后,将委派构造函数的主体。
也就是说,我们专门讨论选择构造函数并调用它-在这种情况下肯定是X(X&&)
。但是另一方面,the very next paragraph说这是初始化:
mem-initializer 中的 expression-list 或 braced-init-list 用于初始化指定的子对象(或者,在如果是委派构造函数,则根据[dcl.init]的初始化规则进行直接初始化。
这看起来很像[dcl.init]/17.6中的保证复制省略示例:
如果初始化程序表达式是prvalue,并且源类型的cv不合格版本与目标程序的类相同,则使用初始化程序表达式初始化目标对象。 [示例:
T x = T(T(T()));
调用T
的默认构造函数来初始化x
。 -示例]
我不确定哪种解释正确,但是拒绝拒绝似乎对我来说显然不是错误的。
为什么这个例子有意义? optional
是libstdc ++中的move构造函数,对于那些可微破坏和可复制/可移动分配的类型(例如您的X
),请执行以下操作:this constructor:
constexpr
_Optional_payload(bool __engaged, _Optional_payload&& __other)
: _Optional_payload(__engaged
? _Optional_payload(__ctor_tag<bool>{},
std::move(__other._M_payload))
: _Optional_payload(__ctor_tag<void>{}))
{ }
此类型已隐式删除了copy和move构造函数,这是因为与成员的联合并不容易实现。因此,如果此委派构造函数必须调用隐式副本构造函数(如clang所认为的那样),则格式错误。如果没有必要,只需调用一个或另一个委托的构造函数,则此调用就可以了。