私有运算符删除会触发GCC和Clang的编译时错误,但不会触发MSVC

时间:2018-06-28 13:32:18

标签: c++ language-lawyer private new-operator delete-operator

this not very well asked duplicate的启发,我认为该问题值得一个新的独立且标题明确的问题。以下代码会触发GCC 8.1.0和Clang 6.0.0的编译错误,但不会触发MSVC 19.00的编译错误:

IMessageProps

来自expr.new

  

如果上述对象初始化的任何部分因引发异常而终止,并且可以找到合适的释放函数,则调用释放函数以释放正在构造对象的内存,此后异常继续传播在new-expression中。 如果找不到明确的匹配释放函数,则传播异常不会导致释放对象的内存。 [注意:分配内存;否则,很可能导致内存泄漏。 — 尾注]

实际上,这并不意味着如果找不到匹配的释放函数export interface IMessageProps { translate: (key: string) => string; } export const message = <P extends IMessageProps>( Component: React.ComponentType<P> ): React.SFC<Pick<P, Exclude<keyof P, keyof IMessageProps>>> => (props: Pick<P, Exclude<keyof P, keyof IMessageProps>>) => { const translate = (key: string): string => messages[key]; return <Component {...props} translate={translate} />; }; class MyComponent extends React.Component<IMessageProps, {}> { render() { return ( <>{this.props.translate('hello.world')}</> ); } } const MyComponentWrapped = message(MyComponent); let d = <MyComponentWrapped /> // works ,则应触发编译错误。还是将其设为私有只会导致类似但却无法访问的问题?哪些编译器是正确的?

2 个答案:

答案 0 :(得分:3)

Visual Studio对于noexcept指示符感到很奇怪。在纸上,它不应该建立。原因是散乱函数的查找独立于分配函数。

  

[expr.new] / 20, 21 and 22

     

如果 new-expression 创建了一个对象或对象数组   类类型,访问和歧义控制已完成分配   函数,释放函数和构造函数。如果    new-expression 创建一个类类型的对象数组,   析构函数可能被调用。

     

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

     

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

根据p20,由于我们正在创建类对象,因此必须查找释放函数 。然后,成功找到了脱配函数,并且该函数是明确的(它是成员)。由于仅在名称查找之后才检查访问说明符,因此应引起错误。 GCC和Clang是正确的。

答案 1 :(得分:3)

这里有两个问题:

  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 ++是错误的(再次)。