Разные значения скобок в C ++? - PullRequest
8 голосов
/ 19 февраля 2011

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

Литье: (int)a или int(a)

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

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

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

Спасибо

Ответы [ 3 ]

22 голосов
/ 19 февраля 2011

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

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

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, которая является ссылкой на массив T с точно 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 в качестве аргумента конструктора, поскольку нет способа, чтобы его можно было рассматривать как прототип функции. & gt; 6.8 и & sect; 8.2 спецификации обрабатывают эти случаи, говоря, что все, что может быть обработано как объявление, будет, и все, что может быть обработано как прототип функции, будет также.

Случай скобок в контексте массива (то есть T (&array)[N]) обрабатывается другой частью логики, потому что в контексте, в котором вы объявляете переменную или определяете параметр, тип которого требует явных скобок , не может быть никакой двусмысленности относительно вашего намерения, поскольку из контекста ясно, что вы называете тип для объявления переменной.

Подведем итог -

  1. Приведения формы T(value) и (T)value идентичны.
  2. Скобки в T (&array)[N] предназначены для предотвращения привязки компилятором & к T вместо array, как предполагалось.
  3. Конкретное использование скобок обычно определяется из контекста, хотя некоторые проблемы могут возникать между объявлениями переменных и прототипами функций.

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

5 голосов
/ 19 февраля 2011

casting (int) a или int (a)

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

int (a) - это конструкция типа intот a до int

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

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

0 голосов
/ 02 июня 2016

Из C ++ 14, Приложение A, список complete , в котором в грамматике могут появляться скобки:

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