Gần đây tôi đã tạo mã ví dụ này để minh họa cho việc sử dụng hàm C++ 11 variadic template.Sự cần thiết của các hàm mẫu khai báo chuyển tiếp
template <typename Head, typename... Tail> void foo (Head, Tail...);
template <typename... Tail> void foo (int, Tail...);
void foo() {}
template <typename... Tail>
void foo (int x, Tail... tail)
{
std :: cout << "int:" << x;
foo (tail...);
}
template <typename Head, typename... Tail>
void foo (Head x, Tail... tail)
{
std :: cout << " ?:" << x;
foo (tail...);
}
foo (int (123), float (123)); // Prints "int:123 ?:123.0"
Nếu hai dòng đầu tiên mà phía trước-tuyên bố foo
bị bỏ qua thì đây sẽ in int:123int:123
để thay thế. Điều này làm ngạc nhiên một lập trình viên C++ có kinh nghiệm và hiểu biết nhất định.
Ông đã thuyết phục rằng các tuyên bố chuyển tiếp không cần thiết vì cơ thể sẽ không được khởi tạo cho đến giai đoạn hai của tra cứu hai pha. Ông nghĩ rằng trình biên dịch (gcc 4.6) có một lỗi.
Tôi tin trình biên dịch là đúng vì hai foo
là different base template functions và lựa chọn mẫu cơ bản cần phải được khóa trong giai đoạn đầu hoặc nếu không bạn có thể vi phạm quy tắc một định nghĩa bằng cách instantiating foo
trước tất cả các phiên bản của nó đã được xác định và sau đó một lần nữa sau đó (xem xét cách trình liên kết giả định rằng các định nghĩa hàm mẫu thừa là giống hệt nhau, có thể hoán đổi cho nhau và loại bỏ được).
Vì vậy, ai là đúng?
Các GOTW trên liên kết độc đáo giải thích như thế nào và tại sao chức năng mẫu không một phần chuyên, nhưng sự tồn tại của chức năng template variadic dường như để thêm vào sự nhầm lẫn - trực giác rằng foo<int,Tail...>
phải là một đặc tả từng phần của foo<Head,Tail...>
mạnh hơn trực giác đối với các hàm phi-variadic, ít nhất là đối với tôi.
FWIW, clang ++ tạo ra kết quả tương tự như gcc. – Cubbi
Tôi không chắc chắn, nhưng có vẻ như khi foo() được gọi, nó sẽ khởi tạo foo (int, Tail ...) mà sau đó cố gắng foo (đuôi ...) và khi tuyên bố về phía trước là không có , nó sẽ không thấy foo (Head, Tail ...) và chỉ có thể chọn foo (int, Tail ...) một ... Bạn cũng có thể thêm một cuộc gọi đến bar (x); vào foo (int, Tail ...) và sau đó khai báo một thanh() sau khi tất cả các hàm foo() ... nó sẽ không được tìm thấy quá – PlasmaHH