由于构造函数中的B基类子项目的复制构造,以下代码无法使用Gcc和Clang进行编译A:
struct B{
B();
B(const B&) =delete;
};
struct A:B{
A():B(B()){} //=> error: use of deleted function...
};
Run Code Online (Sandbox Code Playgroud)
不过根据[class.base.init]/7:
的表达式列表或支撑-INIT列表在MEM-初始化是根据[dcl.init]用于初始化规则用来初始化指定子对象(或者,在一个委派构造,完整的类对象的情况下)直接初始化.
因此初始化规则对于成员或直接基础是相同的.对于成员子对象,Gcc和Clang不使用已删除的复制构造函数:
struct A2:B{
B b;
A2():b(B()){} //=> OK the result object of B() is b
};
Run Code Online (Sandbox Code Playgroud)
这不是Clang和Gcc的编译器错误吗?是不是B应该省略复制构造函数A()?
有趣的是,即使gcc检查复制结构是否格式正确,它也会忽略此复制构造函数调用,请参阅此处的程序集
当调用基类构造函数时,GCC和Clang不会执行C ++ 17的保证复制省略。有关详细信息,请参见此问题和相应的Clang错误报告。
针对错误报告,Richard Smith指出:
这是标准措辞中的一个缺陷。初始化基类子对象时,不能保证复制省略,因为基类可以具有与相应完整对象类型不同的布局。
在什么情况下,基类可以以“无法保证复制省略”的方式具有“与相应完整对象类型不同的布局”?有一个具体的例子可以说明这一点吗?
考虑以下代码:
struct S
{
S() = default;
S(S const&) = delete;
// S(S&&) = delete; // <--- uncomment for a mind-blowing effect:
// MSVC starts compiling EVERY case O_O
};
S foo() { return {}; }
struct X : S
{
// X() : S(foo()) {} // <----- all compilers fail here
};
struct Y
{
S s;
Y() : s(foo()) {} // <----- only MSVC fails here
};
struct Z
{
S s = {}; // ... …Run Code Online (Sandbox Code Playgroud) 请考虑以下代码:
#include <iostream>
struct Thing
{
Thing(void) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
Thing(Thing const &) = delete;
Thing(Thing &&) = delete;
Thing & operator =(Thing const &) = delete;
Thing & operator =(Thing &&) = delete;
};
int main()
{
Thing thing{Thing{}};
}
Run Code Online (Sandbox Code Playgroud)
我希望Thing thing{Thing{}};声明意味着临时对象的建设Thing使用默认的构造函数和建设类thing的对象Thing使用仅仅用创建的临时对象作为参数转移构造类.我希望这个程序被认为是不正确的,因为它包含一个被删除的移动构造函数的调用,即使它可能被省略.标准的class.copy.elision部分似乎也要求这样做:
即使呼叫被省略,也必须可以访问所选的构造函数
通过简化值类别保证复制省略的措辞似乎也不允许.
然而gcc 7.2(以及clang 4也是如此,但VS2017 仍然不支持保证复制省略)将编译此代码,只需移动构造函数调用即可.
在这种情况下哪种行为是正确的?