C++ 中括号的不同含义

发布于 2024-10-18 07:02:50 字数 294 浏览 8 评论 0原文

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

转换:(int)aint(a)

参数传递:

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

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

I am a bit confused with the interpretation of parentheses by the compiler. Can some one please explain what actually happens in such contexts?

Casting: (int)a or int(a)

Parameter passing:

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

Obviously there could be many different contexts where parentheses change the meaning or interpretation. Can some one please explain what exactly is happening behind the curtain? How does the compiler know how to interpret in each context? Is there a general guideline or is it a specific rule for each case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怪我入戏太深 2024-10-25 07:02:50

迂腐船长来救援!

如果您编写

int(value)

This 就是所谓的显式类型转换,并受第 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< /code> 为数组中的元素数量。如果这看起来很混乱(T (&)[N] 到底是什么?),这是因为这个函数接受 T (&)[N] 类型的参数。为了使它更容易阅读,让我们给这个参数一个名称,如下所示:

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

我认为这使得它更容易阅读。但这个声明意味着什么呢?

T (&array)[N]

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

在这种情况下使用括号的原因是,如果您编写

T& array[N]

编译器会将其解析为“一个名为 array 的变量,它是一个由 N 对象组成的数组,其中每个对象都是一个T& 但是,C++ 规范明确不允许引用数组,这将是非法的,这与函数指针类似 - 您编写

void (*functionPointer)()

而不是

void *functionPointer()

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

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

vector<int> v();

创建一个名为 vvector 并已初始化。使用默认构造函数。相反,它将其视为名为 v 的函数的函数原型,该函数不带参数并生成 vector!但是,如果您要编写

vector<int> v(10);

Then 编译器可以明确推断这是一个将 10 作为构造函数参数传递的 vector 声明,因为不可能它可以被视为函数原型。规范的第 6.8 节和第 8.2 节通过规定任何可以被视为声明的东西以及任何可以被视为函数原型的东西来处理这些情况。

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

总结一下 -

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

Captain Pedantic to the Rescue!

If you write

int(value)

This is what's known as an explicit type conversion and is governed by §5.2.3. The exact wording says that

A simple-type-specifier (7.1.5) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4)

(My emphasis). So this means that

int(value)

and

(int)value

are completely identical to one another. It's up to you to pick whichever of these you find easier to write.

As for your second question, in the example you gave with the templates and array, I believe that what you meant to write was something like this.

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

Here, N as well as T is a template parameter, which allows you to pass in any array that you'd like while having the compiler fill in N with the number of elements in the array. In case this looks confusing (what on earth is T (&)[N]?), it's because this function is taking in a parameter of type T (&)[N]. To make this a bit easier to read, let's give this parameter a name, as shown here:

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

I think this makes this a bit easier to read. But what does this declaration mean?

T (&array)[N]

This declares a variable called array that is a reference to an array of Ts of exactly N elements. You can indeed declare references to arrays, just as you can declare pointers to arrays. This is not very common in practice, but in this particular template idiom is a great way of having the compiler infer the size of the array for you as it tries to match the array to the template argument.

The reason for the parentheses in this case is that if you write

T& array[N]

The compiler would parse this as "a variable called array that's an array of N objects, each of which is a T&. However, the C++ spec specifically disallows arrays of references, and this would be illegal. The parentheses explicitly disambiguate this. This is similar to function pointers - you write

void (*functionPointer)()

instead of

void *functionPointer()

To make the compiler realize that the * means that functionPointer is a pointer, rather than a function that returns a void *.

As for how the compiler determines when to treat parentheses in each way, the rules are fairly complex and there are actually a few circumstances in which the compiler will not parse your expression in the intended way. One of these cases is something colloquially referred to as "the most vexing parse" in which the compiler treats what looks like object construction as a function prototype. As an example, this code:

vector<int> v();

Does not create a vector<int> called v initialized using the default constructor. Instead, it treats this as a function prototype for a function called v that takes no arguments and produces a vector<int>! However, if you were to write

vector<int> v(10);

Then the compiler can unambiguously infer that this is a declaration of a vector<int> passing 10 as a constructor argument, because there's no way that it could be treated as a function prototype. §6.8 and §8.2 of the spec handles these cases by saying that anything that can be treated as a declaration will be, and anything that can be treated as a function prototype will be as well.

The case of parentheses in the context of the array (that is, T (&array)[N]) is handled by a different piece of logic because in the context in which you're declaring a variable or defining a parameter whose type requires explicit parenthesis, there can be no ambiguity about your intention because it's clear from context that you're naming a type in order to declare a variable.

To summarize -

  1. Casts of the form T(value) and (T)value are identical.
  2. The parentheses in T (&array)[N] are to prevent the compiler from binding the & to T instead of to array as intended.
  3. The particular use of parenthesis is usually inferred from context, though some issues can come up between variable declarations and function prototypes.
合久必婚 2024-10-25 07:02:50

转换 (int)a 或 int(a)

(int)a 是强制转换

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

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

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

casting (int)a or int(a)

(int)a is a cast

int(a) is the construction of an int, passing in a to the int ctor

Expressions are evaluated according to operators' precedence, arity, and whether the operator is right or left associative. Read the operator precedence chart in your C++ text.

Get a copy of the program c++decl; it reads C++ expressions and outputs an English langauge explanation of the expression. Or read this explanation.

○闲身 2024-10-25 07:02:50

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

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

  • cast-expression: ( type-id )。 )强制转换表达式
  • 后缀表达式:简单类型说明符(表达式-list_opt)
  • 参数和限定符:(参数声明子句)属性说明符-seq_opt cv-qualifier-seq_opt ref-qualifier_opt 异常规范_opt
  • noptr-abstract-declarator:( ptr-abstract-declarator )

From C++14 Appendix A, the complete list of cases where parentheses may appear in the grammar is:

§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 )

Note that:

  • The preprocessor rules for if-group and elif-group do refer to constant-expression.
  • lparen means a ( with no preceding whitespace
  • The rule for raw-string is during lexing, so the ( and ) do not become tokens.
  • Any sequence of valid tokens can appear in a preprocessor group whose condition evaluates to false.

In your question, you use the following:

  • 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 )
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文