Dlaczego C ++ pozwala na umieszczenie nazwy zmiennej w nawiasach podczas deklarowania zmiennej?

84

Na przykład deklaracja taka jak:

int (x) = 0;

Albo nawet to:

int (((x))) = 0;

Natknąłem się na to, ponieważ zdarzyło mi się, że w swoim kodzie miałem fragment podobny do następującego:

struct B
{
};

struct C
{
  C (B *) {}
  void f () {};
};

int main()
{
  B *y;
  C (y);
}

Oczywiście chciałem skonstruować obiekt, Cktóry wtedy zrobiłby coś pożytecznego w swoim destruktorze. Jednak jak to bywa, kompilator traktuje C (y);jako deklarację zmiennej yo typie Ci dlatego wypisuje błąd dotyczący yredefinicji. Ciekawe jest to, że jeśli napiszę to jako C (y).f ()lub jako coś podobnego C (static_cast<B*> (y)), skompiluje się zgodnie z przeznaczeniem. Najlepszym współczesnym obejściem jest {}oczywiście użycie wywołania konstruktora.

Więc jak się później domyśliłem, możliwe jest zadeklarowanie zmiennych takich jak int (x) = 0;lub nawet, int (((x))) = 0;ale nigdy nie widziałem nikogo, kto faktycznie używa takich deklaracji. Więc jestem zainteresowany - jaki jest cel takiej możliwości, bo na razie widzę, że tworzy ona tylko przypadek podobny do osławionej "najbardziej irytującej analizy" i nie dodaje niczego pożytecznego?

Predelnik
źródło
„Celem” tej możliwości jest prawdopodobnie uproszczenie parsera.
molbdnilo
1
@GSerg Zabawne, jak tekst mojego pytania odpowiada na pytanie z drugiej odpowiedzi w Twoim pytaniu z linkiem, ponieważ podam przykład, w którym dopuszczenie takich deklaracji prowadzi do nieoczekiwanych rezultatów :)
Predelnik 16.04.15
wszedł w tę pułapkę: nie wiedział, że mutex został wykomentowany, a następnie przypadkowo napisał nieprawidłową deklarację w nawiasach. // std :: mutex std :: unique_lock <std :: mutex> (m_mutex);
Podać Laurijssen

Odpowiedzi:

80

Grupowanie.

Jako konkretny przykład rozważmy, że można zadeklarować zmienną typu funkcji, taką jak

int f(int);

Jak byś zadeklarował wskaźnik do takiej rzeczy?

int *f(int);

Nie, nie działa! Jest to interpretowane jako funkcja zwracająca int*. Musisz dodać w nawiasach, aby analizować we właściwy sposób:

int (*f)(int);

To samo dotyczy tablic:

int *x[5];   // array of five int*
int (*x)[5]; // pointer to array of five int

źródło
13
I aby uzupełnić odpowiedź: odrzucenie konkretnego przypadku, o który pyta pytający, wymagałoby dodatkowej reguły szczególnej. Obecna definicja tego, jak ()praca w typie jest jednolita w całym typie.
Joseph Mansfield
Tak więc przypadek specjalny dotyczy najbardziej irytującej analizy. Dzieje się tak, ponieważ składnia inicjująca zmienne z argumentami konstruktora została dodana później (chyba w pośpiechu?).
AnArrayOfFunctions
1
@FISOCPP Cóż. . tak. C ++ pojawił się po C. .
iheanyi
Czy ta odpowiedź dotyczy w równym stopniu języka C, a nie tylko C ++?
kdbanman
" zmienna typu funkcji " co? !!
curiousguy
17

Generalnie dopuszcza się używanie nawiasów w takich deklaracjach, ponieważ deklaracja, ze składniowego punktu widzenia, wygląda zawsze tak:

<front type> <specification>;

Na przykład w następującej deklaracji:

int* p[2];

„Typ frontu” to int(nie int*), a „specyfikacja” to * p[2].

Zasada jest taka, że ​​w części dotyczącej specyfikacji można użyć dowolnej liczby nawiasów, ponieważ czasami nieuniknione jest ich ujednoznacznienie. Na przykład:

int* p[2]; // array of 2 pointers to int; same as int (*p[2]);
int (*p)[2]; // pointer to an array of 2 ints

Wskaźnik do tablicy jest rzadkim przypadkiem, jednak ta sama sytuacja ma miejsce ze wskaźnikiem do funkcji:

int (*func(int)); // declares a function returning int*
int (*func)(int); // declares a pointer to function returning int

To jest bezpośrednia odpowiedź na twoje pytanie. Jeśli twoje pytanie dotyczy tego stwierdzenia C(y), to:

  • Umieść nawiasy wokół całego wyrażenia - (C(y))a otrzymasz to, czego chciałeś
  • To stwierdzenie nie robi nic poza stworzeniem tymczasowego przedmiotu, który przestaje istnieć po zakończeniu tej instrukcji (mam nadzieję, że to właśnie zamierzałeś zrobić).
Ethouris
źródło
1
Jak wspomniałem, początkowo robił coś w destruktorze, wydaje mi się, że jest to dość standardowa rzecz, gdy masz kilka funkcji „łańcuchowych” do ustawiania parametrów, a następnie wykonywania całej pracy w destruktorze. Jednak dzięki za jeszcze jedno obejście, myślę, że pisanie {}jest jednak najbardziej trafne.
Predelnik
4
staraj się unikać tworzenia własnej gramatyki i używaj gramatyki zawartej w standardzie. <front-type> <specification>jest mylący i zły. Gramatyka to<declaration-specifier> <declarator>
Steve Cox
Masz rację - nie zajrzałem do standardu, tylko powtórzyłem regułę z głowy. Właściwie w C ++ 11 rolę <declaration-specifier>może pełnić autosłowo kluczowe, więc nie zawsze jest to typ.
Ethouris
@Pravasi Meet: Jeśli zredagowałeś część mojego postu i zmieniłeś nazwy w podanym schemacie składni, zmień również odpowiednio nazwy 3 linie poniżej. W przeciwnym razie nadal istnieją stare nazwy „typ frontu” i „specyfikacja”, dlatego post nie ma sensu.
Ethouris