首页 > 解决方案 > 私有运算符 delete 会触发 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

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

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

标签: c++language-lawyerprivatenew-operatordelete-operator

解决方案


这里有两个问题:

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

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

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

措辞“如果对象初始化的任何部分 [...] 通过抛出异常而终止”表明该段落的其余部分不适用,因为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但在分配之后,因此需要释放内存。

所以我认为 Visual C++ 是错误的(再次)。


推荐阅读