GCC 7.2和Clang 5.0在这种情况下不同意:
struct A;
A foo();
struct A
{
static void bar()
{
foo();
}
private:
~A()=default;
};
A foo()
{
return {};
//GCC error: A::~A() is private in this context.
};
此行为属于" c ++ 17保证(复制省略并且与RVO或NRVO无关)。"
GCC不会编译此代码,但Clang会编译。哪一个错了?
也许这段话说机器人Clang和GCC符合标准[class.temporary]:
当类类型X的对象传递给函数或从函数返回时,如果每个复制构造函数都移动 X的构造函数和析构函数要么是微不足道的,要么是删除的,并且X至少有一个未删除的副本或移动 构造函数,允许实现创建一个临时对象来保存函数参数或 结果对象。临时对象分别由函数参数或返回值构造, 并且函数的参数或返回对象被初始化,就像使用未删除的普通构造函数一样 复制临时(即使该构造函数不可访问或不会被重载决策选中) 执行对象的复制或移动)。 [注意:允许此纬度允许类类型的对象 传递给寄存器中的函数或从函数返回。 - 结束说明]
答案 0 :(得分:1)
我相信这是一个铿锵的错误。
在实现需要时创建临时对象以传递或返回一个简单可复制类型的对象(见下文),[...]
即使临时对象的创建未被评估([expr.prop]),也应该尊重所有语义限制,就好像临时对象已经创建并随后被销毁一样。 [注意:这包括可访问性以及是否已删除,对于所选的构造函数和析构函数。但是,在 decltype-specifier ([expr.call])的操作数的特殊情况下,不引入临时值,因此前述内容不适用于这样的prvalue。 - 结束说明]
foo
返回的复制初始化是一个创建临时对象的上下文,因此仍然必须遵循语义限制 - 其中包括析构函数的可访问性(因为注释有助于说明)。 ~A()
必须在此上下文中可访问,并且不是,因此程序应该是格式错误的。 gcc拒绝是正确的。
答案 1 :(得分:0)
我相信这是C ++ 17中的gcc错误。 根据{{3}}:
自C ++ 17
在以下情况下,编译器是必需的 省略类对象的复制和移动构造甚至 如果复制/移动构造函数和析构函数有 可观察到的副作用。它们不需要存在或可访问, 因为语言规则确保不需要复制/移动操作 地方,甚至概念上:
在初始化中,如果初始化表达式是prvalue而且 cv-源类型的非限定版本与同一类 目的地的类,初始化表达式用于 初始化目标对象。
在函数调用中,如果return语句的操作数是a prvalue和函数的返回类型是一样的 prvalue的类型。
你的函数foo返回一个类型为A的prvalue对象和返回值 foo的类型是A,无论A是否具有可访问的复制/移动 构造函数和析构函数,在C ++ 17中将省略该副本。