Wśród wielu rzeczy, których nauczył mnie Stack Overflow, jest to, co jest znane jako „najbardziej irytująca analiza”, co jest klasycznie przedstawiane za pomocą wiersza takiego jak
A a(B()); //declares a function
Chociaż dla większości wydaje się to intuicyjnie, że jest to deklaracja obiektu a
typu A
, przyjmująca B
obiekt tymczasowy jako parametr konstruktora, w rzeczywistości jest to deklaracja funkcji a
zwracającej an A
, pobierającej wskaźnik do funkcji, która zwraca B
i sama nie przyjmuje żadnych parametrów . Podobnie linia
A a(); //declares a function
również należy do tej samej kategorii, ponieważ zamiast obiektu deklaruje funkcję. Teraz, w pierwszym przypadku, typowym obejściem tego problemu jest dodanie dodatkowego zestawu nawiasów / nawiasów wokół znaku B()
, ponieważ kompilator zinterpretuje to jako deklarację obiektu
A a((B())); //declares an object
Jednak w drugim przypadku zrobienie tego samego prowadzi do błędu kompilacji
A a(()); //compile error
Moje pytanie brzmi: dlaczego? Tak, bardzo dobrze zdaję sobie sprawę, że poprawnym rozwiązaniem jest zmiana tego na A a;
, ale jestem ciekawy, co to jest dodatkowe działanie ()
dla kompilatora w pierwszym przykładzie, który następnie nie działa po ponownym zastosowaniu go w drugi przykład. Czy A a((B()));
obejście zawiera określony wyjątek zapisany w standardzie?
(B())
to tylko wyrażenie w C ++, nic więcej. To nie jest żaden wyjątek. Jedyną różnicą, jaką to robi, jest to, że nie ma możliwości, aby można go było przeanalizować jako typ, więc tak nie jest.A a();
to nie z tej samej kategorii. W przypadku kompilatora nigdy nie ma innego sposobu, aby go przeanalizować: inicjalizator w tym miejscu nigdy nie składa się z pustych nawiasów, więc zawsze jest to deklaracja funkcji.A a();
to nie stanowi przykład najbardziej irytującej parse . To po prostu deklaracja funkcji, tak jak w C.A a;
„ ”jest błędne. To nie da ci inicjalizacji typu POD. Aby uzyskać inicjalizację, napiszA a{};
.Odpowiedzi:
Nie ma oświeconej odpowiedzi, to tylko dlatego, że język C ++ nie zdefiniował jej jako poprawnej składni ... Tak jest z definicji języka.
Jeśli masz wyrażenie wewnątrz, to jest ono ważne. Na przykład:
Jeszcze prościej: ponieważ
(x)
jest prawidłowym wyrażeniem w C ++, a()
nie jest.Aby dowiedzieć się więcej o tym, jak definiuje się języki i jak działają kompilatory, powinieneś zapoznać się z formalną teorią języka, a dokładniej gramatyką bezkontekstową (CFG) i powiązanymi materiałami, takimi jak maszyny skończone. Jeśli jesteś tym zainteresowany, chociaż strony wikipedii nie wystarczą, musisz zdobyć książkę.
źródło
(x)
jest prawidłowym wyrażeniem w C ++, a()
nie jest.Ostatnim rozwiązaniem tego problemu jest przejście do jednolitej składni inicjalizacyjnej C + 11, jeśli możesz.
http://www.stroustrup.com/C++11FAQ.html#uniform-init
źródło
Deklaratory funkcji C.
Przede wszystkim jest C. W C
A a()
jest deklaracja funkcji. Na przykładputchar
ma następującą deklarację. Zwykle takie deklaracje są przechowywane w plikach nagłówkowych, jednak nic nie stoi na przeszkodzie, aby napisać je ręcznie, jeśli wiesz, jak wygląda deklaracja funkcji. Nazwy argumentów są opcjonalne w deklaracjach, więc pominąłem je w tym przykładzie.Pozwala to na pisanie kodu w ten sposób.
C pozwala również na zdefiniowanie funkcji, które przyjmują funkcje jako argumenty, z ładną, czytelną składnią, która wygląda jak wywołanie funkcji (cóż, jest czytelna, o ile nie zwrócisz wskaźnika do funkcji).
Jak wspomniałem, C pozwala na pomijanie nazw argumentów w plikach nagłówkowych, dlatego
output_result
w pliku nagłówkowym wyglądałoby to tak.Jeden argument w konstruktorze
Nie rozpoznajesz tego? Cóż, pozwól, że ci przypomnę.
Tak, to dokładnie ta sama deklaracja funkcji.
A
jestint
,a
jestoutput_result
iB
jestint
.Możesz łatwo zauważyć konflikt C z nowymi funkcjami C ++. Mówiąc dokładniej, konstruktory są nazwą klasy i nawiasami, a alternatywna składnia deklaracji z
()
zamiast=
. Z założenia C ++ stara się być kompatybilny z kodem C, dlatego musi sobie z tym poradzić - nawet jeśli praktycznie nikogo to nie obchodzi. Dlatego stare funkcje C mają pierwszeństwo przed nowymi funkcjami C ++. Gramatyka deklaracji próbuje dopasować nazwę jako funkcję, przed powrotem do nowej składni,()
jeśli to się nie powiedzie.Gdyby jedna z tych funkcji nie istniała lub miała inną składnię (jak
{}
w C ++ 11), ten problem nigdy nie wystąpiłby w przypadku składni z jednym argumentem.Teraz możesz zapytać, dlaczego
A a((B()))
działa. Cóż, zadeklarujmyoutput_result
z bezużytecznymi nawiasami.To nie zadziała. Gramatyka wymaga, aby zmienna nie była umieszczona w nawiasach.
Jednak C ++ oczekuje tutaj standardowego wyrażenia. W C ++ możesz napisać następujący kod.
I następujący kod.
C ++ oczekuje, że wyrażenie wewnątrz nawiasów będzie ... cóż ... wyrażeniem, w przeciwieństwie do tego, jakiego oczekuje typ C. Nawiasy tutaj nic nie znaczą. Jednak przez wstawienie bezużytecznych nawiasów deklaracja funkcji C nie jest dopasowywana, a nowa składnia może być dopasowana poprawnie (co po prostu oczekuje wyrażenia, takiego jak
2 + 2
).Więcej argumentów w konstruktorze
Z pewnością jeden argument jest fajny, ale co z dwoma? Nie chodzi o to, że konstruktorzy mogą mieć tylko jeden argument. Jedną z wbudowanych klas, która przyjmuje dwa argumenty, jest
std::string
To wszystko jest w porządku (technicznie rzecz biorąc, najbardziej irytująca byłaby analiza, gdyby była zapisana jako
std::string wat(int(), char())
, ale bądźmy szczerzy - kto by to napisał? Ale załóżmy, że ten kod ma irytujący problem. Można by założyć, że musisz wstawić wszystko w nawiasach.Nie całkiem.
Nie jestem pewien, dlaczego g ++ próbuje przekonwertować
char
naconst char *
. W każdym razie konstruktor został wywołany tylko z jedną wartością typuchar
. Nie ma przeciążenia, które ma jeden argument typuchar
, dlatego kompilator jest zdezorientowany. Możesz zapytać - dlaczego argument jest typu char?Tak,
,
tutaj jest operator przecinka. Operator przecinka przyjmuje dwa argumenty i podaje argument po prawej stronie. Nie jest to naprawdę przydatne, ale należy je poznać z mojego wyjaśnienia.Zamiast tego, aby rozwiązać najbardziej irytującą analizę, potrzebny jest następujący kod.
Argumenty znajdują się w nawiasach, a nie w całym wyrażeniu. W rzeczywistości tylko jedno z wyrażeń musi znajdować się w nawiasach, ponieważ wystarczy nieznacznie oderwać się od gramatyki języka C, aby użyć funkcji C ++. Rzeczy doprowadzają nas do punktu zerowego argumentów.
Zero argumentów w konstruktorze
Być może zauważyłeś
eighty_four
funkcję w moim wyjaśnieniu.Tak, dotyczy to również najbardziej irytującej analizy. To poprawna definicja, którą najprawdopodobniej widzieliście, jeśli utworzyliście pliki nagłówkowe (a powinniście). Dodanie nawiasów nie rozwiązuje tego problemu.
Dlaczego to jest takie? Cóż,
()
to nie jest wyrażenie. W C ++ musisz umieścić wyrażenie w nawiasach. Nie możesz pisaćauto value = ()
w C ++, ponieważ()
nic nie znaczy (a nawet gdyby tak było, jak pusta krotka (zobacz Python), byłby to jeden argument, a nie zero). Praktycznie oznacza to, że nie możesz używać skróconej składni bez użycia składni C ++ 11{}
, ponieważ nie ma wyrażeń, które można by umieścić w nawiasach, a gramatyka języka C dla deklaracji funkcji będzie zawsze miała zastosowanie.źródło
Mógłbyś zamiast tego
posługiwać się
źródło
int a = int();
inicjujea
od 0,int a;
pozostawiaa
niezainicjowane. Prawidłowym obejściem jest użycieA a = {};
dla agregacji,A a;
gdy domyślna inicjalizacja robi to, co chcesz, iA a = A();
we wszystkich innych przypadkach - lub po prostu używajA a = A();
konsekwentnie. W C ++ 11 po prostu użyjA a {};
Najbardziej wewnętrzne pareny w twoim przykładzie byłyby wyrażeniem, aw C ++ gramatyka definiuje an
expression
jako jedenassignment-expression
lub inny,expression
po którym następuje przecinek i innyassignment-expression
(Dodatek A.4 - Podsumowanie gramatyki / Wyrażenia).Gramatyka dalej definiuje
assignment-expression
wyrażenie jako jeden z kilku innych typów wyrażeń, z których żaden nie może być niczym (lub tylko białą spacją).Więc powodem, dla którego nie możesz mieć,
A a(())
jest po prostu to, że gramatyka na to nie pozwala. Nie mogę jednak odpowiedzieć, dlaczego ludzie, którzy stworzyli C ++, nie pozwolili na to szczególne użycie pustych parenów jako specjalnego przypadku - przypuszczam, że woleliby nie umieszczać tak specjalnego przypadku, gdyby istniał rozsądna alternatywa.źródło