Khai báo hàm C
Trước hết, có C. Trong C, A a()
là khai báo hàm. Ví dụ, putchar
có tuyên bố sau. Thông thường, các khai báo như vậy được lưu trữ trong các tệp tiêu đề, tuy nhiên không có gì ngăn bạn viết chúng theo cách thủ công, nếu bạn biết cách khai báo của hàm trông như thế nào. Tên đối số là tùy chọn trong khai báo, vì vậy tôi đã bỏ qua nó trong ví dụ này.
int putchar(int);
Điều này cho phép bạn viết mã như thế này.
int puts(const char *);
int main() {
puts("Hello, world!");
}
C cũng cho phép bạn xác định các hàm lấy các hàm làm đối số, với cú pháp dễ đọc trông giống như một lệnh gọi hàm (tốt, nó có thể đọc được, miễn là bạn sẽ không trả về một con trỏ cho hàm).
#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);
}
Như tôi đã đề cập, C cho phép bỏ qua các tên đối số trong các tệp tiêu đề, do đó output_result
sẽ trông như thế này trong tệp tiêu đề.
int output_result(int());
Một đối số trong hàm tạo
Bạn không nhận ra cái đó à? Vâng, hãy để tôi nhắc bạn.
A a(B());
Đúng, đó chính xác là khai báo hàm giống nhau. A
là int
, a
là output_result
, và B
là int
.
Bạn có thể dễ dàng nhận thấy xung đột của C với các tính năng mới của C ++. Để chính xác, các hàm tạo là tên lớp và dấu ngoặc đơn và thay thế cú pháp khai báo ()
thay thế =
. Theo thiết kế, C ++ cố gắng tương thích với mã C, và do đó nó phải xử lý trường hợp này - ngay cả khi thực tế không ai quan tâm. Do đó, các tính năng C cũ được ưu tiên hơn các tính năng C ++ mới. Ngữ pháp của khai báo cố gắng khớp tên dưới dạng hàm, trước khi hoàn nguyên cú pháp mới ()
nếu thất bại.
Nếu một trong những tính năng đó không tồn tại hoặc có một cú pháp khác (như {}
trong C ++ 11), thì vấn đề này sẽ không bao giờ xảy ra đối với cú pháp với một đối số.
Bây giờ bạn có thể hỏi tại sao A a((B()))
làm việc. Vâng, chúng ta hãy tuyên bố output_result
với dấu ngoặc đơn vô dụng.
int output_result((int()));
Nó sẽ không hoạt động. Ngữ pháp yêu cầu biến không nằm trong ngoặc đơn.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
Tuy nhiên, C ++ mong đợi biểu hiện tiêu chuẩn ở đây. Trong C ++, bạn có thể viết đoạn mã sau.
int value = int();
Và mã sau đây.
int value = ((((int()))));
C ++ mong đợi biểu thức bên trong dấu ngoặc đơn là ... cũng ... biểu thức, trái ngược với loại C mong đợi. Dấu ngoặc đơn không có nghĩa gì ở đây. Tuy nhiên, bằng cách chèn các dấu ngoặc đơn vô dụng, khai báo hàm C không khớp và cú pháp mới có thể được khớp đúng (chỉ đơn giản là mong đợi một biểu thức, chẳng hạn như 2 + 2
).
Thêm đối số trong hàm tạo
Chắc chắn một đối số là tốt, nhưng hai là gì? Không phải là các nhà xây dựng có thể chỉ có một đối số. Một trong các lớp dựng sẵn có hai đối số làstd::string
std::string hundred_dots(100, '.');
Đây là tất cả tốt và tốt (về mặt kỹ thuật, nó sẽ có hầu hết các phân tích bực tức nếu nó được viết như std::string wat(int(), char())
, nhưng hãy trung thực - ai sẽ viết điều đó? Nhưng hãy giả sử mã này có một vấn đề khó chịu. Bạn sẽ cho rằng bạn phải đặt mọi thứ trong ngoặc đơn.
std::string hundred_dots((100, '.'));
Không hoàn toàn như vậy.
<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>::
^
Tôi không chắc tại sao g ++ cố gắng chuyển đổi char
sang const char *
. Dù bằng cách nào, hàm tạo được gọi chỉ với một giá trị kiểu char
. Không có tình trạng quá tải có một đối số kiểu char
, do đó trình biên dịch bị nhầm lẫn. Bạn có thể hỏi - tại sao đối số là kiểu char?
(100, '.')
Vâng, ,
đây là một toán tử dấu phẩy. Toán tử dấu phẩy nhận hai đối số và đưa ra đối số bên phải. Nó không thực sự hữu ích, nhưng đó là điều cần biết để giải thích cho tôi.
Thay vào đó, để giải quyết các phân tích vexing nhất, mã sau đây là cần thiết.
std::string hundred_dots((100), ('.'));
Các đối số là trong ngoặc đơn, không phải toàn bộ biểu thức. Trong thực tế, chỉ cần một trong các biểu thức là trong ngoặc đơn, vì nó đủ để tách khỏi ngữ pháp C một chút để sử dụng tính năng C ++. Mọi thứ đưa chúng ta đến điểm không tranh luận.
Không có đối số trong hàm tạo
Bạn có thể đã nhận thấy eighty_four
chức năng trong lời giải thích của tôi.
int eighty_four();
Vâng, điều này cũng bị ảnh hưởng bởi các phân tích bực tức nhất. Đó là một định nghĩa hợp lệ và rất có thể bạn đã thấy nếu bạn đã tạo các tệp tiêu đề (và bạn nên). Thêm dấu ngoặc đơn không sửa nó.
int eighty_four(());
Tại sao lại như vậy? Vâng, ()
không phải là một biểu thức. Trong C ++, bạn phải đặt một biểu thức giữa các dấu ngoặc đơn. Bạn không thể viết auto value = ()
bằng C ++, vì ()
không có nghĩa gì cả (và ngay cả khi đã làm, như bộ dữ liệu trống (xem Python), đó sẽ là một đối số, không phải bằng không). Thực tế điều đó có nghĩa là bạn không thể sử dụng cú pháp tốc ký mà không sử dụng cú pháp của C ++ 11 {}
, vì không có biểu thức nào để đặt trong ngoặc đơn và ngữ pháp C cho khai báo hàm sẽ luôn được áp dụng.
(B())
chỉ là một biểu thức C ++, không có gì hơn. Đó không phải là bất kỳ loại ngoại lệ. Sự khác biệt duy nhất mà nó tạo ra là không có cách nào nó có thể được phân tích thành một loại, và vì vậy nó không.