私有运营商删除会触发 GCC 和 Clang 的编译时错误,但不会在 MSVC 上触发编译时错误



受这个不太好的重复问题的激励,我相信这个问题值得一个新的独立明确标题的问题。以下代码会触发 GCC 8.1.0 和 Clang 6.0.0 的编译错误,但不会在 MSVC 19.00 中触发编译错误:

class X {
public:
X() /* noexcept */ { }    
private:
static void operator delete(void*) { }
};
int main() { 
X* x = new X{}; 
}

来自 expr.new:

如果上述对象初始化的任何部分通过抛出异常而终止,并且可以找到合适的释放函数,则调用释放函数以释放构造对象的内存,之后异常继续在新表达式的上下文中传播。如果找不到明确的匹配释放函数,则传播异常不会导致释放对象的内存。[注意:当调用的分配函数不分配内存时,这是合适的;否则,它可能会导致内存泄漏。

实际上,这并不意味着如果找不到匹配的释放函数::operator delete就应该触发编译错误。或者,将其设为私有是否只会导致可以找到但无法访问类似的东西?哪些编译器是正确的?

这里有两个问题:

  1. 即使operator delete是私密的,如何找到它?

C++首先尝试在任何地方查找名称;检查访问保护是稍后阶段。
因此,您的operator delete已找到,但无法访问。

  1. 为什么在构造函数noexcept时我的operator delete必须可访问?

措辞"如果对象初始化的任何部分 [...]通过抛出异常终止"表明该段的其余部分由于noexcept而不适用。

但是,正如"...的任何部分"所建议的那样,在分配和进入构造函数之间(在评估初始化器时(或退出构造函数之后(在销毁初始化器时(可能存在异常。

考虑

struct Y
{
Y() {}
Y(const Y&) { throw "sorry"; }
};
class X {
public:
X(Y y) noexcept { }    
private:
static void operator delete(void*) { }
};
int main() { 
Y y;
X* x = new X{y}; 
}

其中Y复制构造函数在你输入X的构造函数之前抛出,但在分配之后,所以需要释放内存。

所以我认为视觉C++(再次(是错误的。

Visual Studio对noexcept说明符很奇怪。在纸面上,它不应该建立。原因是 decllocate 函数独立于分配函数进行查找。

[expr.new]/20, 21 和 22

如果新表达式创建一个对象或对象数组 对分配进行类类型、访问和歧义控制 函数、释放函数和构造函数。如果new-expression创建一个类类型的对象数组,即 可能会调用析构函数。

如果上述对象初始化的任何部分终止于 抛出异常和合适的释放函数可以是 找到后,调用释放函数以释放其中的内存 正在构造对象,之后异常继续 在新表达式的上下文中传播。如果没有明确的 可以找到匹配的释放函数,传播异常 不会导致释放对象的内存。

如果 new 表达式以一元::​运算符开头,则 在全局范围内查找释放函数的名称。 否则,如果分配的类型是类类型T或数组 其中,在T.如果此查找找不到名称,或者分配的类型为 不是类类型或其数组,释放函数的名称是 在全球范围内查找。

根据 p20,必须查找释放函数,因为我们正在创建一个类对象。然后成功找到去allcoation函数,并且是明确的(它是成员(。由于访问说明符仅在名称查找后进行检查,因此这应该会导致错误。GCC和Clang是正确的。

最新更新