new int;中的new是操作符吗?


83

new int;in等表达式int * x = new int;是一个新表达式。术语“新运算符”似乎可以与“新表达式”互换使用,例如在以下问题中:“新运算符”和“新运算符”之间的区别?

new在新表达式中使用的关键字是运算符是否正确?为什么或者为什么不?

如果不是,是否有另一个理由来调用新表达式“ new operator”?

我很难找到构成操作员的权威定义。

我已经了解了为对象分配内存的new运算符和最终可能会调用的“ new expression”之间的区别operator new


4
@Rogue-事情new就是这样sizeof。而且我认为大多数C ++社区都将其sizeof视为运算符。sizeof在描述其效果时,标准甚至会引用“运算符”。
StoryTeller-Unslander Monica

2
@ StoryTeller-UnslanderMonica因此,从我的第二条评论(现在移至答案)继续sizeof,我说那将是表达式的运算符sizeof(<type>)。并且+是一元表达式的运算符<type> <op> <type>。至少这是我的要点/解释,请随时纠正我。
盗贼

2
@Rogue-的确如此。这就是为什么我认为规范性文本在引用一个作为运算符而不是另一个作为运算符时是相当武断的。
StoryTeller-Unslander Monica

1
接受答案的附带说明:无论是否new为运算符,表达式和运算符之间都有区别。例如,new int是一种表达式,它可以评估某些内容。如果new将其视为运算符,则仅运算符的“新”部分new int是该运算符(即,它是自己单独的东西)。从概念上讲,操作员是要对其他事物起作用的事物。
FilipMilovanović

Answers:


137

newinnew int不被视为运算符。它也被认为不是操作员。

C ++标准对于构成“运算符”的含义确实含糊不清,甚至不一致。当列出运算符(在词法分析和预处理过程中定义)时,它会与“标点符号”(如()一起列出,但总体上从没有真正给出任何标点符号规则。它同时new列为关键字和运算符。它sizeof在关键字集中列出,但不在运算符集中列出,但后来将其称为运算符。

这里的要点是,C ++标准委员会不太关心将词汇世界分为“运算符”和“非运算符”。这是因为实际上没有任何需要。没有适用于所有运营商或所有非运营商的语法规则。重要集合(如可重载运算符的集合)是分别给出的;一次给出诸如二进制算术表达式之类的语法规则。

基本上,“运算符”不是对C ++语言形式上有意义的类别,因此任何明确的答案都将基于它的“感觉”。如果愿意,可以将其称为运算符,但也可以将其称为关键字(或标点符号!),并且语言标准也不会与您不同。


6
C和C ++标准在很多术语上都是草率的,因为他们希望读者能够推断出存在差距或模棱两可的含义,而不是试图忽略是否可以使用这种遗漏来证明“优化”是合理的。 “如果这些标准的作者认为荒谬至极,则无需明确禁止它们。
超级猫

16

newnew表达式中使用的关键字是运算符是否正确?为什么或者为什么不?

不可以。new-expression中newin是标识new-expression的关键字。

一个新的表达 要求operator newoperator new[]获得存储。它还会初始化该存储,并在初始化抛出时使用(operator deleteoperator delete[])取消分配该存储。

有一个明显的区别,即operator new仅被称为可重载的用户可替换函数,而new-expression的作用不仅仅在于调用此函数。

参考:7.6.2.8/10 [expr.new]

新表达可以通过调用分配函数获得用于对象存储([basic.stc.dynamic.allocation])。如果new表达式因引发异常而终止,则可以通过调用释放函数来释放存储。如果分配的类型是非数组类型,则分配函数的名称为operator new,而释放函数的名称为operator delete。如果分配的类型是数组类型,则分配函数的名称为operator new[],而释放函数的名称为operator delete[]


考虑一下反例,我们定义了两者

T operator+(T, T);
void* T::operator new(std::size_t);

对于某些类型T,然后以两种形式之一加法:

T a, b;
T c = a + b;
T d = T::operator +(a, b);

是相同的。前缀表示法只是用于操作员调用的语法糖。

但是,分配的行为却大不相同:

T *p = new T;
// does much more than
void *v = T::operator new(sizeof(T));

因此,调用new表达式语法糖来调用并不合理operator new。因此,new关键字不只是选择要调用的函数。不能,或者必须提到operator delete可能也被调用的函数。


2
operator new是可重载的运算符” —是吗?我不这么认为:operator + 不是可重载运算符—+是,您可以通过称为的函数来定义重载operator +
康拉德·鲁道夫

1
不,我通过指出运算符是作为new表达式的一部分调用的特定函数来回答它。new-expression是由关键字标识的表达式,它可以找到合适的运算符来调用。
没用的

1
我仍然看不到它如何解决new关键字在new表达式中出现的状态。
John Bollinger

6
确实,new容易受到运算符重载的事实表明,答案应该是yesnew(本身)是运算符。
约翰·布林格

2
该标准仅指也调用运算符的表达式。如果没有递归的话,它们就不可能是同一件事,因此简约的解释是表达式和​​运算符完全相同,并且只有标准所说的是真实的。
没用的

12

我只称其为新表达式,以避免与void* operator new ( std::size_t count )仅在新表达式 发票处理过程中分配内存(分配内存,启动生命周期,调用构造函数)的过程混淆。

问题new在于它不仅仅调用operator new。这是一个有点混乱,因为x + y+只有通话operator +


9

这由[expr.new]控制,它明确区分了new表达式和新表达式如何通过调用名为的分配函数来获取存储operator new。特别是从 [expr.new] / 8 [强调我的]:

新表达式可以得到存储通过调用分配函数为对象([basic.stc.dynamic.allocation])。如果new表达式因引发异常而终止,则可以通过调用释放函数来释放存储。如果分配的类型是非数组类型,则分配函数的名称为operator new,而释放函数的名称为operator delete。如果分配的类型是数组类型,则分配函数的名称为operatornew[],解除分配函数的名称为operator delete[]


new在新表达式中使用的关键字是运算符是否正确?

特别是,[expr.new] / 4的示例(尽管不是标准的)将此功能描述为运算符功能; “ [...]new操作员”

[...]相反,可以使用new运算符的显式括号版本[...]


3
谢谢你的回答。从我收到的答案的数量开始,似乎似乎没有一种严格的语言可以用一种或另一种方式回答这个问题。因此,非规范性文本最终可能是最好的证据。
弗朗索瓦·安德里厄

8

没有。

嗯,有点是的,在存在谁考虑人们的意识newnew int是一个运营商。但是,这种观点(大部分)与标准不符。

首先,[lex.operators/1]列出该语言的运算符。也不要愚蠢地认为它们只是“预处理运算符”;在词汇运算符的意义上,不存在这种区别。这也没有意义,因为您不能(例如)++使用宏。

new实际上是一个关键字(每个[lex.key/1])。

接下来,让我们看一下new-expression本身。这是事情变得更加艰难的地方。例如,以下措辞如下[expr.new/4]

相反,可以使用新运算符的显式括号括起来的版本来创建复合类型的对象

我认为这是一个编辑错误,因为它与上面提供的定义不一致,并且在该部分的其他任何地方都没有发生。

然后我们来讨论运算符重载。在其用于操作符声明的语法生成中,列出事物​​(包括new)的终端称为操作符[over.oper.general/1])。我认为我们不必为此担心。语法中的终端名称从未打算引入术语的定义。毕竟,您具有并表示_不必是按位AND操作;它可以只是一个相等表达式

and-expression
   等式
    & 等式等式

像这样定义语法是很常见的,当然这并不意味着每个等式表达式都可以被视为对逐位AND运算符的调用。

最后,有人声称以下措辞(也在运算符重载部分中)证明,new现在孤立地以某种方式神奇地成为了运算符:

运营商new[]delete[](),和[]被从一个以上的令牌形成

我对他们说,不仅new没有列出,而且显然是在更广泛的意义上使用“操作员”一词,即“可能超载的事物”,尽管new它本身仍然不是操作员。它也在非规范性注释中,该注释应告诉您所有您需要了解的内容。

而且,正如您指出的那样,我们已经考虑operator new了其他问题。


1
[lex.operators]显然是不完整的(并不意味着是完整的!),因为[expr.sizeof]规范地说sizeof也是一个运算符。[expr.throw]的含义不太明确,throw但仍然throw提到的“操作数” ,这意味着它throw是一个运算符。
康拉德·鲁道夫

7

虽然我知道您在寻找语义上的答案,但据我所知,它是一个“关键字”(因为它显然是保留的),并且我想我们都知道这只是C ++的内存分配构造。就是说,当我想到C ++的“运算符”时,我倾向于想到具有设置的输入/输出的函数,这些输入/输出可以为特定类型覆盖。new对于所有类型都是相同的(更令人困惑的是,它可以被覆盖)。

不知道官方的cppreference是多少,但是:

new表达式通过调用适当的分配函数来分配存储。如果type是非数组类型,则函数的名称为operator new。如果type是数组类型,则函数的名称为operator new []

https://en.cppreference.com/w/cpp/language/new

随之而来的似乎new <type>是分配内存的表达式,而new该表达式的运算符。

使用相同的逻辑:

  • sizeof是表达式的“运算符” sizeof(<type>)(尽管sizeof是“编译时”运算符,与我们习惯的有点不同)
  • + 是表达式的运算符 <type> <operator> <type>
  • throw 是表达式的运算符 throw <throwaable>

按照这种逻辑,[]()也是运算符吗?是for吗 是throw
康拉德·鲁道夫

1
@KonradRudolph是的,我要说,[]()构成独立的运算符。同样for,绝对是一个关键字,但是我最不愿意成为操作员的是能够被覆盖的第一部分。从技术上讲sizeof它不是运算符,C ++确实如此,因为它更像是一个预处理表达式。并且throw是表达式的运算符throw <throwable>
Rogue

[]()在定义lambda时,我特别要结合使用。但是您的回答也很有趣,因为(在C ++中)答案取决于上下文:在中foo()()是(可重载!)运算符;在(1 + 2)它是不是在所有的运营商(在其他一些语言,尤其是R,这是不同的)。无论如何,可覆盖的标准还不够。有很多事情++用C是明确经营者尽管不是重载(.:::? sizeofS,等等)。
康拉德·鲁道夫

1
嗯,[]()作为lambda是难以定义的野兽(特别是因为我的C ++速度不够快)。所以在这里我仍然要说[] ()构成lambda表达式的不同运算符,但是我不确定您是否会争辩说它们不能被覆盖,或者执行/声明lambda的行为是否构成了对基本lambda的覆盖(例如在Java中)。语言设计在这里开始成为一门超越科学的艺术。至于( ... ),它是我使用过的大多数语言的分隔符,但是我会很快对R real感到迷惑
Rogue

2
附带说明一下,在R中,一切都是函数,可以重新定义一切。没有全面的规范性参考,我也不完全确定括号被视为运算符,但是可以重新定义它们。实际上,该表达式(a + b)在语义上与函数调用`(`(a + b),或者实际上是`(((++(a,b))),其中x可以用来命名x语法上符合函数调用,而不是特殊的函数(例如infix运算符)
Konrad Rudolph
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.