我对编译器对圆括号的解释有点困惑。有人能解释一下在这样的上下文中实际发生了什么吗?铸造:(int)a或int(a)参数传递:
(int)a
int(a)
template <typename t> int size(t (&)[n]){return n;}
字符串很明显,在很多不同的上下文中,括号会改变含义或解释。有人能解释一下幕后到底发生了什么吗?编译器如何知道如何在每个上下文中进行解释?是否有一个通用的指导方针,或者是针对每种情况的特定规则?
f8rj6qna1#
学究船长来救援了!如果你写
int(value)
字符串这就是所谓的 * 显式类型转换 *,受§5.2.3的约束。简单类型说明符(7.1.5)后跟括号中的expression-list构造给定表达式列表的指定类型的值。如果表达式列表是单个表达式,则类型转换表达式等效于(在定义性方面,如果在含义方面定义)相应的强制转换表达式(5.4)(My强调)。这意味着
型和
(int)value
型是完全相同的。您可以自行选择其中较容易撰写的一个。至于你的第二个问题,在你给出的模板和数组的例子中,我相信你的意思是这样的。
template <typename T, size_t N> size_t (T (&)[N]) { return N; }
型在这里,N和T都是一个模板参数,它允许您传入任何您想要的数组,同时让编译器用数组中的元素数填充N。(T (&)[N]到底是什么?),因为这个函数接受的是一个T (&)[N]类型的参数。为了更容易理解,我们给予这个参数一个名字,如下所示:
N
T
T (&)[N]
template <typename T, size_t N> size_t (T (&array)[N]) { return N; }
型我想这让这篇文章读起来更容易一点。但是这个声明是什么意思呢?
T (&array)[N]
型这声明了一个名为array的变量,它引用了一个T的数组,其中正好有N个元素。实际上,您可以声明对数组的引用,就像您可以声明指向数组的指针一样。实际上,这并不常见。但是在这个特定的模板中,当编译器试图将数组与模板参数。在本例中使用括号的原因是,如果您将
array
T& array[N]
型编译器会将其解析为“一个名为array的变量,它是一个N对象的数组,每个对象都是一个T&。但是,C++规范特别禁止引用数组,这是非法的。括号明确地消除了这一点。这与函数指针类似-您可以编写
T&
void (*functionPointer)()
型而不是
void *functionPointer()
型要使编译器认识到,*意味着functionPointer是一个指针,而不是一个返回void *的函数。至于编译器如何确定何时以每种方式处理括号,规则相当复杂,实际上有几种情况下编译器不会以预期的方式解析表达式。其中一种情况就是俗称的“最令人烦恼的解析”,编译器将看起来像对象构造的内容视为函数原型。例如,以下代码:
*
functionPointer
void *
vector<int> v();
型
不创建一个名为v的vector<int>,该vector<int>使用默认构造函数进行初始化。相反,它会将其视为一个名为v的函数的函数原型,该v函数不接受任何参数并生成一个vector<int>!但是,如果要编写
v
vector<int>
vector<int> v(10);
型然后编译器可以明确地推断这是一个vector<int>的声明,它将10作为构造函数参数传递,因为它不可能被视为函数原型。任何可以被视为函数原型的东西也是如此。数组(即T (&array)[N])上下文中括号的情况由不同的逻辑处理,因为在声明变量或定义其类型需要显式括号的参数的上下文中,您的意图不会有任何歧义,因为从上下文可以清楚地看出,您命名类型是为了声明变量。总结一下-
10
T(value)
(T)value
&
h4cxqtbf2#
int(a)= int(a)(int)a是一个类型转换int(a)是一个int的构造,将a传递给int ctor表达式根据运算符的优先级、arity以及运算符是右关联还是左关联来计算。阅读C文本中的运算符优先级表。获取程序cdecl的副本;它读取C++表达式并输出表达式的英语解释。Or read this explanation.
yvfmudvl3#
从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
)
在您的问题中,您使用以下内容:
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 )
3条答案
按热度按时间f8rj6qna1#
学究船长来救援了!
如果你写
字符串
这就是所谓的 * 显式类型转换 *,受§5.2.3的约束。
简单类型说明符(7.1.5)后跟括号中的expression-list构造给定表达式列表的指定类型的值。如果表达式列表是单个表达式,则类型转换表达式等效于(在定义性方面,如果在含义方面定义)相应的强制转换表达式(5.4)
(My强调)。这意味着
型
和
型
是完全相同的。您可以自行选择其中较容易撰写的一个。
至于你的第二个问题,在你给出的模板和数组的例子中,我相信你的意思是这样的。
型
在这里,
N
和T
都是一个模板参数,它允许您传入任何您想要的数组,同时让编译器用数组中的元素数填充N
。(T (&)[N]
到底是什么?),因为这个函数接受的是一个T (&)[N]
类型的参数。为了更容易理解,我们给予这个参数一个名字,如下所示:型
我想这让这篇文章读起来更容易一点。但是这个声明是什么意思呢?
型
这声明了一个名为
array
的变量,它引用了一个T
的数组,其中正好有N
个元素。实际上,您可以声明对数组的引用,就像您可以声明指向数组的指针一样。实际上,这并不常见。但是在这个特定的模板中,当编译器试图将数组与模板参数。在本例中使用括号的原因是,如果您将
型
编译器会将其解析为“一个名为
array
的变量,它是一个N
对象的数组,每个对象都是一个T&
。但是,C++规范特别禁止引用数组,这是非法的。括号明确地消除了这一点。这与函数指针类似-您可以编写型
而不是
型
要使编译器认识到,
*
意味着functionPointer
是一个指针,而不是一个返回void *
的函数。至于编译器如何确定何时以每种方式处理括号,规则相当复杂,实际上有几种情况下编译器不会以预期的方式解析表达式。其中一种情况就是俗称的“最令人烦恼的解析”,编译器将看起来像对象构造的内容视为函数原型。例如,以下代码:
型
不创建一个名为
v
的vector<int>
,该vector<int>
使用默认构造函数进行初始化。相反,它会将其视为一个名为v
的函数的函数原型,该v
函数不接受任何参数并生成一个vector<int>
!但是,如果要编写型
然后编译器可以明确地推断这是一个
vector<int>
的声明,它将10
作为构造函数参数传递,因为它不可能被视为函数原型。任何可以被视为函数原型的东西也是如此。数组(即
T (&array)[N]
)上下文中括号的情况由不同的逻辑处理,因为在声明变量或定义其类型需要显式括号的参数的上下文中,您的意图不会有任何歧义,因为从上下文可以清楚地看出,您命名类型是为了声明变量。总结一下-
T(value)
和(T)value
的铸造形式是相同的。T (&array)[N]
中的括号是为了防止编译器将&
系结至T
,而非预期的array
。1.括号的特殊用法通常是从上下文推断出来的,尽管在变量声明和函数原型之间可能会出现一些问题。
h4cxqtbf2#
int(a)= int(a)
(int)a是一个类型转换
int(a)是一个int的构造,将a传递给int ctor
表达式根据运算符的优先级、arity以及运算符是右关联还是左关联来计算。阅读C文本中的运算符优先级表。
获取程序cdecl的副本;它读取C++表达式并输出表达式的英语解释。Or read this explanation.
yvfmudvl3#
从C++14附录A中,括号可能出现在语法中的情况的 * 完整 * 列表是:
字符串
请注意:
if-group
和elif-group
的预处理器规则引用constant-expression
。lparen
表示前面没有空格的(
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 )
个