Различные значения скобок в С++?

Меня немного смущает интерпретация скобок компилятором. Может кто-нибудь объяснить, что на самом деле происходит в таких контекстах?

Кастинг: (int)a или int(a)

Передача параметров:

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

Очевидно, может быть много разных контекстов, в которых скобки изменяют значение или интерпретацию. Может кто-нибудь объяснить, что именно происходит за занавеской? Откуда компилятор знает, как интерпретировать в каждом контексте? Есть ли какие-то общие рекомендации или для каждого случая есть свои правила?

Спасибо


person Kiran    schedule 19.02.2011    source источник
comment
Угловые скобки < и > не являются скобками.   -  person Rafe Kettler    schedule 19.02.2011
comment
Истинный. Я не говорю о них как о скобках здесь. Спасибо за редактирование.   -  person Kiran    schedule 19.02.2011


Ответы (3)


Капитан Педантик спешит на помощь!

Если вы пишете

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;
}

Здесь N, так же как и T, является параметром шаблона, который позволяет вам передавать любой массив, который вы хотите, при этом компилятор заполняет N количеством элементов в массиве. На случай, если это сбивает с толку (что такое T (&)[N]?), это потому, что эта функция принимает параметр типа T (&)[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&. Однако спецификация C++ специально запрещает массивы ссылок, и это было бы недопустимо. Круглые скобки явно устраняют неоднозначность. аналогично указателям на функции - вы пишете

void (*functionPointer)()

вместо

void *functionPointer()

Чтобы компилятор понял, что * означает, что functionPointer является указателем, а не функцией, которая возвращает void *.

Что касается того, как компилятор определяет, когда обрабатывать круглые скобки тем или иным образом, правила довольно сложны, и на самом деле есть несколько обстоятельств, при которых компилятор не будет анализировать ваше выражение предполагаемым образом. Одним из таких случаев является то, что в просторечии называется «наиболее неприятным синтаксическим анализом», когда компилятор обрабатывает то, что выглядит как конструкция объекта, как прототип функции. Например, этот код:

vector<int> v();

Не создает vector<int> с именем v, инициализированным с помощью конструктора по умолчанию. Вместо этого он рассматривает это как прототип функции с именем v, которая не принимает аргументов и выдает vector<int>! Однако, если бы вы написали

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. Конкретное использование круглых скобок обычно выводится из контекста, хотя некоторые проблемы могут возникать между объявлениями переменных и прототипами функций.

Надеюсь это поможет!

person templatetypedef    schedule 19.02.2011
comment
Потрясающий. Спасибо за подробное объяснение. Я собираюсь перечитывать ее снова и снова, несколько раз, пока она не синхронизируется с моим мыслительным процессом. - person Kiran; 19.02.2011

приведение (int)a или int(a)

(int)a - это бросок

int(a) — это конструкция int, переходящая через a в int ctor

Выражения оцениваются в соответствии с приоритетом операторов, арностью и тем, является ли оператор правой или левой ассоциативностью. Прочтите таблицу приоритетов операторов в своем тексте на C++.

Получить копию программы c++decl; он читает выражения C++ и выводит объяснение выражения на английском языке. Или прочитайте это объяснение.

person tpdi    schedule 19.02.2011
comment
Это правда. Но это не относится ко второму случаю. Как интерпретирует компилятор в этом случае? Спасибо - person Kiran; 19.02.2011
comment
Также полезен cdecl.org, но он не полностью поддерживает C++. (В качестве теста он правильно описал ссылку, но сказал, что она не поддерживается в C). - person RastaJedi; 11.08.2016

полный список случаев, когда в грамматике могут появляться круглые скобки, приведен в Приложении A C++14:

§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 )
person o11c    schedule 02.06.2016