受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
,则应触发编译错误。还是将其设为私有只会导致类似但却无法访问的问题?哪些编译器是正确的?
答案 0 :(得分:3)
Visual Studio对于noexcept
指示符感到很奇怪。在纸上,它不应该建立。原因是散乱函数的查找独立于分配函数。
如果 new-expression 创建了一个对象或对象数组 类类型,访问和歧义控制已完成分配 函数,释放函数和构造函数。如果 new-expression 创建一个类类型的对象数组, 析构函数可能被调用。
如果上述对象初始化的任何部分终止于 抛出异常和合适的释放函数可以是 找到后,调用释放函数以释放其中的内存 对象被构造后,异常继续 在new表达式的上下文中传播。如果没有明确 可以找到匹配的释放函数,传播异常 不会导致释放对象的内存。
如果new表达式以一元
::
运算符开头,则 在全局范围内查找释放函数的名称。 否则,如果分配的类型是类类型T
或数组 其中,在以下范围内查找释放函数的名称T
。如果此查找未能找到名称,或者分配的类型为 不是类类型或其数组,则释放函数的名称为 在全球范围内查找。
根据p20,由于我们正在创建类对象,因此必须查找释放函数 。然后,成功找到了脱配函数,并且该函数是明确的(它是成员)。由于仅在名称查找之后才检查访问说明符,因此应引起错误。 GCC和Clang是正确的。
答案 1 :(得分:3)
这里有两个问题:
operator delete
是私有的,如何找到? C ++首先尝试在任何地方找到一个名称;检查访问保护是后面的阶段。
因此,您的operator delete
已找到,但无法访问。
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 ++是错误的(再次)。