Đầu tiên, hãy xem xét mã C ++ này:
#include <stdio.h>
struct foo_int {
void print(int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void print(const char* x) {
printf("str %s\n", x);
}
};
struct foo : foo_int, foo_str {
//using foo_int::print;
//using foo_str::print;
};
int main() {
foo f;
f.print(123);
f.print("abc");
}
Như mong đợi theo Tiêu chuẩn, điều này không thể biên dịch, vì print
được xem xét riêng biệt trong mỗi lớp cơ sở nhằm mục đích giải quyết quá tải, và do đó các lệnh gọi là không rõ ràng. Đây là trường hợp của Clang (4.0), gcc (6.3) và MSVC (17.0) - xem kết quả chốt chặn ở đây .
Bây giờ hãy xem xét đoạn mã sau, sự khác biệt duy nhất của đoạn mã đó là chúng tôi sử dụng operator()
thay vì print
:
#include <stdio.h>
struct foo_int {
void operator() (int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void operator() (const char* x) {
printf("str %s\n", x);
}
};
struct foo : foo_int, foo_str {
//using foo_int::operator();
//using foo_str::operator();
};
int main() {
foo f;
f(123);
f("abc");
}
Tôi hy vọng kết quả sẽ giống với trường hợp trước, nhưng không phải vậy - trong khi gcc vẫn phàn nàn, Clang và MSVC có thể biên dịch khoản phạt này!
Câu hỏi số 1: Ai đúng trong trường hợp này? Tôi mong đợi nó là gcc, nhưng thực tế là hai trình biên dịch không liên quan khác đưa ra một kết quả nhất quán khác nhau ở đây khiến tôi tự hỏi liệu tôi có thiếu thứ gì đó trong Tiêu chuẩn hay không và mọi thứ khác nhau đối với các toán tử khi chúng không được gọi bằng cú pháp hàm.
Cũng lưu ý rằng nếu bạn chỉ bỏ ghi chú một trong các using
khai báo mà không phải khai báo kia, thì cả ba trình biên dịch sẽ không biên dịch được, bởi vì chúng sẽ chỉ xem xét hàm được đưa vào using
trong quá trình giải quyết quá tải và do đó một trong các lệnh gọi sẽ không thành công do loại không phù hợp. Nhớ lấy điều này; chúng ta sẽ quay lại với nó sau.
Bây giờ hãy xem xét đoạn mã sau:
#include <stdio.h>
auto print_int = [](int x) {
printf("int %d\n", x);
};
typedef decltype(print_int) foo_int;
auto print_str = [](const char* x) {
printf("str %s\n", x);
};
typedef decltype(print_str) foo_str;
struct foo : foo_int, foo_str {
//using foo_int::operator();
//using foo_str::operator();
foo(): foo_int(print_int), foo_str(print_str) {}
};
int main() {
foo f;
f(123);
f("foo");
}
Một lần nữa, giống như trước đây, ngoại trừ bây giờ chúng tôi không định nghĩa operator()
rõ ràng, nhưng thay vào đó lấy nó từ một kiểu lambda. Một lần nữa, bạn mong đợi kết quả nhất quán với đoạn mã trước đó; và điều này đúng đối với trường hợp cả hai using
khai báo đều được ghi chú hoặc nếu cả hai đều được bỏ ghi chú . Nhưng nếu bạn chỉ nhận xét một mà không nhận xét khác, mọi thứ lại đột ngột khác : bây giờ chỉ có MSVC phàn nàn như tôi mong đợi, trong khi Clang và gcc đều cho rằng điều đó ổn - và sử dụng cả hai thành viên kế thừa để giải quyết quá tải, mặc dù chỉ có một được đưa vào bởi using
!
Câu hỏi số 2: Ai đúng trong trường hợp này? Một lần nữa, tôi muốn nó là MSVC, nhưng tại sao cả Clang và gcc lại không đồng ý? Và, quan trọng hơn, tại sao đoạn mã này lại khác với đoạn mã trước? Tôi mong đợi kiểu lambda hoạt động giống hệt như kiểu được xác định thủ công với quá tải operator()
...