Deklaracja C ++ i nawiasy - dlaczego?


32

Temat był wcześniej omawiany , ale to nie jest duplikat.

Kiedy ktoś pyta o różnicę między decltype(a)i decltype((a)), jak zwykle odpowiedź brzmi - ajest to zmienna, (a)jest wyrazem. Uważam tę odpowiedź za niezadowalającą.

Po pierwsze, ajest to także wyrażenie. Opcje wyrażenia podstawowego obejmują między innymi:

  • ( wyrażenie )
  • wyrażenie id

Co ważniejsze, frazowanie dla dekltype uwzględnia nawiasy bardzo, bardzo wyraźnie :

For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1)  if e is an unparenthesized id-expression naming a structured binding, ...
(1.2)  otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3)  otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4)  otherwise, ...

Pozostaje więc pytanie. Dlaczego nawiasy są traktowane inaczej? Czy ktoś jest zaznajomiony z dokumentami technicznymi lub dyskusjami komitetów? Bezpośrednie rozważenie nawiasów prowadzi do przekonania, że ​​nie jest to niedopatrzenie, więc musi istnieć techniczny powód, dla którego tęsknię.


2
„zwykła odpowiedź brzmi - a jest zmienną, (a) jest wyrażeniem„ Co to znaczy „ (a)jest wyrażeniem i ajest wyrażeniem i zmienną”.
HolyBlackCat

Odpowiedzi:


18

To nie jest niedopatrzenie. Interesujące jest to, że w Decltype i auto (wersja 4) (N1705 = 04-0145) znajduje się oświadczenie:

Reguły dotyczące dekltype teraz wyraźnie stwierdzają, że decltype((e)) == decltype(e)(zgodnie z sugestią EWG).

Ale w Decltype (wersja 6): proponowane sformułowanie (N2115 = 06-018) jedną ze zmian jest

Wyrażenie w nawiasie wewnątrz dekltype nie jest uważane za id-expression.

Nie ma żadnego uzasadnienia w sformułowaniu, ale przypuszczam, że jest to rodzaj rozszerzenia dekltypy przy użyciu nieco innej składni, innymi słowy, zamierzano rozróżnić te przypadki.

Użycie do tego pokazane jest w C ++ draft9.2.8.4:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

Co jest naprawdę interesujące, to jak działa ze returnstwierdzeniem:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

Moje Visual Studio 2019 sugeruje, żebym usunął zbędny nawias, ale w rzeczywistości zmieniają się, w decltype((i))które zmiany zwracają wartość, int&co powoduje, że jest to UB, ponieważ zwraca odwołanie do zmiennej lokalnej.


Dziękujemy za dokładne wskazanie pochodzenia! Okazało się, że nie tylko odmienność została określona inaczej, ale początkowo tak było. Dokument rev-6 wyraźnie dodaje to zabawne zachowanie w nawiasach, ale pomija uzasadnienie :(. Myślę, że to jest tak blisko odpowiedzi, jak byśmy teraz otrzymali ..
Ofek Shilon

A jednak nikt nie zaproponował rozwiązania tego głupiego problemu poprzez uczynienie go dwoma odrębnymi słowami kluczowymi dla dwóch różnych funkcji? Jeden z największych błędów komitetu (który historycznie popełnił wiele niewymuszonych błędów).
ciekawy

13

Dlaczego nawiasy są traktowane inaczej?

Nawiasy nie są traktowane inaczej. To niepozorowane wyrażenie id jest traktowane inaczej.

Gdy nawiasy są obecne, obowiązują regularne reguły dla wszystkich wyrażeń. Typ i kategoria wartości są wyodrębniane i kodowane według typu decltype.

Istnieje specjalny przepis, abyśmy mogli łatwiej pisać użyteczny kod. Kiedy aplikujemy decltypedo nazwy zmiennej (członkowskiej), zwykle nie chcemy jakiegoś typu, który reprezentuje właściwości zmiennej, gdy jest traktowany jako wyrażenie. Zamiast tego chcemy tylko typu, za pomocą którego deklarowana jest zmienna, bez konieczności stosowania do tego mnóstwa cech typu. I to jest dokładnie to, co decltypemamy nam dać.

Jeśli zależy nam na właściwościach zmiennej jako wyrażeniu, nadal możemy ją dość łatwo uzyskać za pomocą dodatkowej pary nawiasów.


Więc dla intczłonka iz a, decltype(a.i)jest intnatomiast decltype((a.i))to int&(zakładając, że anie jest const)? Skoro wyrażenie a.imożna przypisać?
n314159

1
@ n314159 - To sedno tego. Wyrażenie a.ijest nie stałą wartością, więc otrzymujesz typ odniesienia dla wartości stałej (a.i).
StoryTeller - Unslander Monica

Dlaczego „reguły regularne dla wszystkich wyrażeń” wskazują, że ich typ jest odwołaniem? Dlaczego „właściwości zmiennej jako wyrażenia” obejmują właściwość bycia referencją? Czy to jakaś naturalna wartość domyślna?
Ofek Shilon

1
@OfekShilon - Ich typ nie jest odnośnikiem. Rodzaj wyrażenia nigdy nie jest analizowany jako odniesienie . Ale dectlype może rozpoznać tylko typ i ma na celu poinformowanie nas nie tylko o typie wyrażenia, ale także o jego kategorii wartości. Kategoria wartości jest skodyfikowana według typów referencyjnych. lvalues ​​są &, xvalues ​​są &&, a prvalues ​​nie są typami referencyjnymi.
StoryTeller - Unslander Monica

@StoryTeller dokładnie, nie ma „naturalnej” różnicy między typami wyrażeń i wyrażeń innych niż wyrażenia. Co czyni to rozróżnienie sztucznym.
Ofek Shilon

1

W wersjach wcześniejszych niż C ++ 11 język potrzebuje narzędzi, aby uzyskać dwa różne rodzaje informacji :

  • rodzaj wyrażenia
  • typ zmiennej, jaka została zadeklarowana

Ze względu na charakter tych informacji funkcje musiały zostać dodane w języku (nie można tego zrobić w bibliotece). Oznacza to nowe słowa kluczowe. Standard mógł wprowadzić do tego dwa nowe słowa kluczowe. Na przykład, exprtypeaby uzyskać typ wyrażenia i decltypeuzyskać typ deklaracji zmiennej. To byłaby jasna, szczęśliwa opcja.

Jednak komitet standardowy zawsze starał się unikać wprowadzania nowych słów kluczowych w języku, aby zminimalizować uszkodzenie starego kodu. Kompatybilność wsteczna jest podstawową filozofią języka.

Więc z c ++ 11 mamy tylko jedno słowo kluczowe używany dla dwóch różnych rzeczy: decltype. Różnice między tymi dwoma zastosowaniami polega na tym, że traktujemy decltype(id-expression)inaczej. Była to świadoma decyzja komitetu, (mały) kompromis.


Pamiętam, jak to słyszałem podczas rozmowy CPP. Nie mam jednak nadziei na znalezienie źródła. Byłoby wspaniale, gdyby ktoś to znalazł.
bolov

1
C ++ historycznie dodało kilka nowych słów kluczowych, wiele bez podkreślenia i niektóre z jednym angielskim słowem. Racjonalność jest naprawdę absurdalna.
ciekawy

@curiousguy każde wprowadzone słowo kluczowe będzie kolidowało z istniejącymi symbolami użytkownika, dlatego komitet przykłada dużą wagę do dodawania nowych zarezerwowanych słów kluczowych do języka. To nie jest absurdalne imo.
bolov

Nieprawda: exportzostał wprowadzony. Jeśli możesz export(wcześniej wszystkie szablony były domyślnie „eksportowane”), możesz mieć takie rzeczy jak decltypei constexpr. Oczywiście dodanie registerw innym języku byłoby problematyczne.
ciekawy

@bolov Dziękujemy! (1) Czy to spekulacja lub wiedza? Czy zdajesz sobie sprawę z dyskusji, w których motywacją było unikanie dodatkowego słowa kluczowego? (2) Czy możesz podać przykład, w którym wyrażenie id musi być traktowane inaczej, jeśli jest używane „jako wyrażenie”? (Co to w ogóle oznacza?)
Ofek Shilon
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.