C++中括号的不同含义

14

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

类型转换:(int)a 或者 int(a)

参数传递:

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

显然,括号在许多不同的情境下可能会改变意义或解释。有人能解释一下幕后到底发生了什么吗?编译器如何知道如何在每个情境中解释?是有一个通用指导方针还是每种情况都有具体规则?

3
尖括号 "<" 和 ">" 不是圆括号。 - Rafe Kettler
没错。我这里不是指它们为括号。感谢您的编辑。 - Kiran
3个回答

27
Captain Pedantic to the Rescue!
如果你写下
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 (&)[N]是什么鬼?),那是因为这个函数接受的参数类型是T (&)[N]。为了让这个参数更容易阅读,让我们给它起个名字,如下所示:
template <typename T, size_t N>
size_t (T (&array)[N]) {
    return N;
}

我觉得这样阅读起来会更容易一些。但是这个声明是什么意思呢?
T (&array)[N]

这声明了一个名为array的变量,它是一个指向由T类型的N个元素组成的数组的引用。你确实可以声明数组的引用,就像你可以声明数组的指针一样。在实践中,这并不常见,但在这个特定的模板惯用语中,这是一种让编译器为你推断数组大小的好方法,因为它试图将数组与模板参数匹配。
在这种情况下,括号的原因是如果你写成:
T& array[N]

编译器会将此解析为“一个名为array的变量,它是一个包含N个对象的数组,每个对象都是T&类型的”。然而,C++规范明确禁止引用数组,因此这是非法的。括号明确消除了歧义。这与函数指针类似-你可以这样写
void (*functionPointer)()

替代

void *functionPointer()

为了让编译器意识到*表示functionPointer是一个指针,而不是返回void *的函数。
至于编译器如何确定何时以每种方式处理括号,规则相当复杂,实际上有几种情况下编译器会无法按照预期的方式解析表达式。其中一种情况被俗称为“最令人困惑的解析”,在这种情况下,编译器将看起来像对象构造的内容视为函数原型。例如,以下代码:
vector<int> v();

不会创建一个名为v的vector,也不会使用默认构造函数进行初始化。相反,它将其视为一个名为v的函数原型,该函数不带参数并产生一个vector!但是,如果你写下以下内容
vector<int> v(10);

然后编译器可以明确地推断出这是一个声明一个vector<int>并将10作为构造函数参数传递的语句,因为它不可能被视为函数原型。 规范的§6.8和§8.2通过指出任何可以被视为声明的内容都将被视为声明,任何可以被视为函数原型的内容也将被视为函数原型来处理这些情况。
在数组的上下文中(即T (&array)[N]),括号的情况由不同的逻辑处理,因为在声明变量或定义需要显式括号的参数类型的上下文中,你的意图是明确的,因为从上下文中清楚地表明你正在命名一个类型以便声明一个变量。
总结一下 -
  1. 形式为 T(value)(T)value 的转换是相同的。
  2. T (&array)[N] 中的括号是为了防止编译器将 & 绑定到 T 而不是预期的 array
  3. 括号的具体使用通常可以从上下文中推断出来,尽管在变量声明和函数原型之间可能会出现一些问题。

太棒了。感谢您详细的解释。我会多次阅读,直到它与我的思维过程同步。 - Kiran
“形式为T(value)(T)value的强制转换是相同的。”这并不是真的。后者可以执行reinterpret_cast,而前者则不能。例如,使用前者将void指针转换为特定指针是行不通的,并且会导致编译器错误。 - Jimmy T.
@JimmyT。我在2011年写下了这个答案,当时我使用的是C++03规范作为参考。我想知道(a)我的答案一直是错误的还是(b)这在C++03中是正确的,但在后来的版本中是错误的。你有什么想法吗? - templatetypedef
@templatetypedef 再次查看规范,似乎你是对的,据我所知,新规范并没有改变这一点。 - Jimmy T.

5

强制类型转换 (int)a 或 int(a)

(int)a 是一种类型转换

int(a) 是构造一个 int 类型,将 a 传递给 int 构造函数

表达式按照运算符的优先级、元数以及运算符是右结合还是左结合进行求值。请阅读您的 C++ 教材中的运算符优先级表。

获取 c++decl 程序的副本;它可以读取 C++ 表达式并输出该表达式的英语解释。 或者阅读此解释。


那是真的。但这不适用于第二种情况。编译器在这种情况下如何解释?谢谢 - Kiran
另外,http://cdecl.org很有用,但不完全支持C ++。(作为测试,它正确地描述了引用,但说它在C中不受支持)。 - RastaJedi

0

从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-expression
  • lparen 表示没有前导空格的 (
  • raw-string 的规则是在词法分析期间进行的,因此 () 不会成为标记。
  • 任何有效标记序列都可以出现在条件计算为 false 的预处理器组中。

在你的问题中,你使用了以下内容:
  • 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 )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接