C ++中括号的不同含义?

Posted

技术标签:

【中文标题】C ++中括号的不同含义?【英文标题】:Different meanings of parentheses in C++? 【发布时间】:2011-06-30 06:05:05 【问题描述】:

我对编译器对括号的解释有点困惑。有人可以解释一下在这种情况下实际发生的情况吗?

选角:(int)aint(a)

参数传递:

template <typename t>
int size(t (&)[n])return n;

显然,在许多不同的上下文中,括号会改变含义或解释。有人可以解释一下幕后到底发生了什么吗?编译器如何知道如何在每个上下文中进行解释?是否有一般指导方针,还是针对每种情况的特定规则?

谢谢

【问题讨论】:

尖括号&lt;&gt; 不是圆括号。 是的。我在这里不是将它们称为括号。感谢您的编辑。 【参考方案1】:

铸造 (int)a 或 int(a)

(int)a 是一个演员表

int(a) 是 int 的构造,将 a 传递给 int ctor

表达式是根据运算符的优先级、数量以及运算符是右结合还是左结合来评估的。阅读 C++ 文本中的运算符优先级图表。

获取程序c++decl的副本;它读取 C++ 表达式并输出该表达式的英语语言解释。 Or read this explanation.

【讨论】:

确实如此。但这不适用于第二种情况。在这种情况下编译器如何解释?谢谢 cdecl.org 也很有用,但不完全支持 C++。 (作为测试,它正确地描述了一个引用,但说它在 C 中不受支持)。【参考方案2】:

学究队长救援!

如果你写

int(value)

这就是所谓的显式类型转换,由 §5.2.3 管理。确切的措辞是这样的

一个简单类型说明符(7.1.5)后跟一个带括号的表达式列表构造一个给定表达式列表的指定类型的值。 如果表达式列表是单个表达式,则类型转换表达式等效于(在定义上,如果在含义上定义)对应的强制转换表达式 (5.4)

(我的重点)。所以这意味着

int(value)

(int)value

彼此完全相同。由您选择您认为更容易编写的任何一个。

关于你的第二个问题,在你给出的模板和数组的例子中,我相信你的意思是这样写的。

template <typename T, size_t N>
size_t (T (&)[N]) 
    return N;

这里,NT 是一个模板参数,它允许您传入任何您想要的数组,同时让编译器用数组中的元素数填充 N。如果这看起来令人困惑(T (&amp;)[N] 到底是什么?),那是因为这个函数接受了 T (&amp;)[N] 类型的参数。为了便于阅读,我们给这个参数起个名字,如下所示:

template <typename T, size_t N>
size_t (T (&array)[N]) 
    return N;

我认为这使阅读更容易一些。但是这个声明是什么意思呢?

T (&array)[N]

这声明了一个名为 array 的变量,它是对 Ts 的数组的引用,该数组恰好是 N 元素。您确实可以声明对数组的引用,就像您可以声明指向数组的指针一样。这在实践中并不常见,但在这个特定的模板习语中,它是让编译器在尝试将数组与模板参数匹配时为您推断数组大小的好方法。

在这种情况下括号的原因是如果你写

T& array[N]

编译器会将其解析为“一个名为 array 的变量,它是 N 对象的数组,每个对象都是 T&amp;。但是,C++ 规范明确禁止引用数组,这是非法的.括号明确地消除了歧义。这类似于函数指针-您编写

void (*functionPointer)()

而不是

void *functionPointer()

让编译器意识到* 意味着functionPointer 是一个指针,而不是一个返回void * 的函数。

至于编译器如何确定何时以各种方式处理括号,规则相当复杂,实际上在某些情况下编译器不会以预期的方式解析您的表达式。其中一种情况是通俗地称为“最令人烦恼的解析”,其中编译器将看起来像对象构造的东西视为函数原型。例如,这段代码:

vector<int> v();

不会创建一个名为vvector&lt;int&gt;,并使用默认构造函数进行初始化。相反,它将它视为一个名为v 的函数的函数原型,该函数不接受任何参数并生成一个vector&lt;int&gt;!但是,如果你要写

vector<int> v(10);

然后编译器可以明确推断这是 vector&lt;int&gt; 的声明,将 10 作为构造函数参数传递,因为它不可能被视为函数原型。规范的 §6.8 和 §8.2 处理这些情况时说,任何可以被视为声明的东西都将是,任何可以被视为函数原型的东西也将是。

数组上下文中的括号大小写(即T (&amp;array)[N])由不同的逻辑处理,因为在上下文中您要声明变量或定义类型需要显式括号的参数,你的意图不会有歧义,因为从上下文中可以清楚地看出,你命名一个类型是为了声明一个变量。

总结一下-

    T(value)(T)value 形式的转换是相同的。 T (&amp;array)[N] 中的括号是为了防止编译器按预期将&amp; 绑定到T 而不是array。 括号的特殊用途通常是从上下文中推断出来的,但在变量声明和函数原型之间可能会出现一些问题。

希望这会有所帮助!

【讨论】:

太棒了。感谢您的详细解释。我会一遍又一遍地阅读它,直到它同步到我的思想过程中。【参考方案3】:

从 C++14 附录 A 中,完整在语法中可能出现括号的情况列表是:

§A.14 Preprocessing directives
control-line: # define identifier lparen identifier-list_opt ) replacement-list new-line
control-line: # define identifier lparen ... ) replacement-list new-line
control-line: # define identifier lparen identifier-list , ... ) replacement-list new-line

§A.2 Lexical conventions
raw-string: " d-char-sequence_opt ( r-char-sequence_opt ) d-char-sequence_opt "

§A.4 Expressions
primary-expression: ( expression )
lambda-declarator: ( parameter-declaration-clause ) mutable_opt exception-specification_opt attribute-specifier-seq_opt trailing-return-type_opt
postfix-expression: const_cast < type-id > ( expression )
postfix-expression: dynamic_cast < type-id > ( expression )
postfix-expression: postfix-expression ( expression-list_opt )
postfix-expression: reinterpret_cast < type-id > ( expression )
postfix-expression: simple-type-specifier ( expression-list_opt )
postfix-expression: static_cast < type-id > ( expression )
postfix-expression: typeid ( expression )
postfix-expression: typeid ( type-id )
postfix-expression: typename-specifier ( expression-list_opt )
unary-expression: alignof ( type-id )
unary-expression: sizeof ( type-id )
unary-expression: sizeof ... ( identifier )
new-expression: ::_opt new new-placement_opt ( type-id ) new-initializer_opt
new-placement: ( expression-list )
new-initializer: ( expression-list_opt )
noexcept-expression: noexcept ( expression )
cast-expression: ( type-id ) cast-expression

§A.5 Statements
selection-statement: if ( condition ) statement
selection-statement: if ( condition ) statement else statement
selection-statement: switch ( condition ) statement
iteration-statement: do statement while ( expression ) ;
iteration-statement: for ( for-init-statement condition_opt ; expression_opt ) statement
iteration-statement: for ( for-range-declaration : for-range-initializer ) statement
iteration-statement: while ( condition ) statement

§A.6 Declarations
static_assert-declaration: static_assert ( constant-expression , string-literal ) ;
decltype-specifier: decltype ( auto )
decltype-specifier: decltype ( expression )
asm-definition: asm ( string-literal ) ;
alignment-specifier: alignas ( assignment-expression ..._opt )
alignment-specifier: alignas ( type-id ..._opt )
attribute-argument-clause: ( balanced-token-seq )
balanced-token: ( balanced-token-seq )

§A.7 Declarators
noptr-declarator: ( ptr-declarator )
parameters-and-qualifiers: ( parameter-declaration-clause ) attribute-specifier-seq_opt cv-qualifier-seq_opt ref-qualifier_opt exception-specification_opt
noptr-abstract-declarator: ( ptr-abstract-declarator )
initializer: ( expression-list )

§A.10 Special member functions
mem-initializer: mem-initializer-id ( expression-list_opt )

§A.11 Overloading
operator-function-id: operator ( )

§A.13 Exception handling
handler: catch ( exception-declaration ) compound-statement
dynamic-exception-specification: throw ( type-id-list_opt )
noexcept-specification: noexcept ( constant-expression )

注意:

if-groupelif-group 的预处理器规则确实引用了 constant-expressionlparen 表示 ( 前面没有空格 raw-string 的规则是在词法分析期间,因此 () 不会成为标记。 任何有效标记序列都可以出现在条件评估为假的预处理器组中。

在您的问题中,您使用以下内容:

cast-expression: ( type-id ) cast-expression postfix-expression: simple-type-specifier ( expression-list_opt ) parameters-and-qualifiers: ( parameter-declaration-clause ) attribute-specifier-seq_opt cv-qualifier-seq_opt ref-qualifier_opt exception-specification_opt noptr-abstract-declarator: ( ptr-abstract-declarator )

【讨论】:

以上是关于C ++中括号的不同含义?的主要内容,如果未能解决你的问题,请参考以下文章

c语言中形参和实参的区别?

c语言中形参和实参的区别是啥?

C语言有啥特点和其他语言如BASIC、FORTRAN等相比有何不同

概率论里面这个括号里面上m下n是啥啊(里面有图)

c语言的基本数据类型都有啥,麻烦知道用大括号分类,非常感谢

c语言中关系运算符和自加自减运算优先级哪个高