Deklaratory funkcji C.
Przede wszystkim jest C. W C A a()jest deklaracja funkcji. Na przykład putcharma 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.
int putchar(int);
Pozwala to na pisanie kodu w ten sposób.
int puts(const char *);
int main() {
puts("Hello, world!");
}
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).
#include <stdio.h>
int eighty_four() {
return 84;
}
int output_result(int callback()) {
printf("Returned: %d\n", callback());
return 0;
}
int main() {
return output_result(eighty_four);
}
Jak wspomniałem, C pozwala na pomijanie nazw argumentów w plikach nagłówkowych, dlatego output_resultw pliku nagłówkowym wyglądałoby to tak.
int output_result(int());
Jeden argument w konstruktorze
Nie rozpoznajesz tego? Cóż, pozwól, że ci przypomnę.
A a(B());
Tak, to dokładnie ta sama deklaracja funkcji. Ajest int, ajest output_resulti Bjest int.
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óż, zadeklarujmy output_resultz bezużytecznymi nawiasami.
int output_result((int()));
To nie zadziała. Gramatyka wymaga, aby zmienna nie była umieszczona w nawiasach.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
Jednak C ++ oczekuje tutaj standardowego wyrażenia. W C ++ możesz napisać następujący kod.
int value = int();
I następujący kod.
int value = ((((int()))));
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, jeststd::string
std::string hundred_dots(100, '.');
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.
std::string hundred_dots((100, '.'));
Nie całkiem.
<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
from <stdin>:1:
/usr/include/c++/4.8/bits/basic_string.tcc:212:5: error: initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
basic_string<_CharT, _Traits, _Alloc>::
^
Nie jestem pewien, dlaczego g ++ próbuje przekonwertować charna const char *. W każdym razie konstruktor został wywołany tylko z jedną wartością typu char. Nie ma przeciążenia, które ma jeden argument typu char, dlatego kompilator jest zdezorientowany. Możesz zapytać - dlaczego argument jest typu char?
(100, '.')
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.
std::string hundred_dots((100), ('.'));
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_fourfunkcję w moim wyjaśnieniu.
int eighty_four();
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.
int eighty_four(());
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.
(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.