2009-05-04 41 views
22

Vui lòng xem xét mã này:SFINAE với các tham số kiểu mảng hoặc kiểu hàm không hợp lệ?

template<typename T> 
char (&f(T[1]))[1]; 

template<typename T> 
char (&f(...))[2]; 

int main() { char c[sizeof(f<void()>(0)) == 2]; } 

tôi mong đợi nó làm SFINAE và chosing sự quá tải thứ hai, kể từ khi thay thế T vào T[1] sản lượng

void [1]() 

Đó là một kiểu không hợp lệ, tất nhiên. Việc điều chỉnh các kiểu tham số (array-> pointer) được thực hiện sau khi thay thế các tham số mẫu thành các tham số hàm và kiểm tra các kiểu kết quả hợp lệ như 14.8.2 [temp.deduct] mô tả.

Nhưng cả Comeau và GCC đều không biên dịch được ở trên. Cả hai đều có chẩn đoán khác nhau.

Comeau nói:

"ComeauTest.c", line 2: error: array of functions is not allowed char (&f(T[1]))[1];

GCC nói (phiên bản 4.3.3):

error: ISO C++ forbids zero-size array c

Ý nghĩa, GCC không thất bại trong việc thay thế, nhưng nó chọn quá tải đầu tiên của f, trả lại một sizeof của 1, thay vì không thay thế nó lên phía trước như Comeau.

Trình biên dịch nào là đúng và mã của tôi có hợp lệ không? Vui lòng tham khảo hoặc trích dẫn phần Chuẩn thích hợp trong câu trả lời của bạn. Cảm ơn!


Cập nhật: Các tiêu chuẩn riêng của mình chứa như một ví dụ trong danh sách ở 14.8.2/2. Tôi không biết tại sao tôi bỏ qua nó lần đầu tiên:

template <class T> int f(T[5]); 
int I = f<int>(0); 
int j = f<void>(0); // invalid array 

Trong khi ví dụ này là chỉ cung cấp thông tin, nó cho thấy ý định của tất cả những đoạn bí ẩn và dường như hiển thị các mã trên nên làm việc và từ chối sự quá tải đầu tiên.

+18

litb yêu cầu làm rõ tiêu chuẩn là mâu thuẫn :) – JaredPar

+1

tôi hỏi tương tự trên usenet, nhưng đăng câu hỏi ở đây quá, để nó có thể được lưu trữ trên SO và mọi người sẽ tìm thấy nó khi tìm kiếm trên SO. Liên kết với usenet: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/78f8cd8cf27778e3 –

+13

Tôi nghĩ litb LÀ tiêu chuẩn ...? –

Trả lời

12

Một lưu ý nhỏ, mặc dù rất hiếm, tôi đã tìm thấy một số trường hợp mà tôi tin rằng trình biên dịch Comeau có sai - mặc dù, những dịp rất hiếm hoi mà nó luôn luôn giá trị đôi và ba kiểm tra giả định của bạn!

Tôi có thể có lý do về hành vi của g ++. Tôi không chắc chắn của nó định chính xác khi nào loại thông số được điều chỉnh:

xem xét như sau:

template<typename T> 
struct A 
{ 
    void bar (T[10]); 
}; 

template<typename T> 
void A<T>::bar (T*) 
{ 
} 

Định nghĩa về 'bar' là hợp pháp, như "T [10]" phân rã đến "T * ". Tôi làm không thấy bất kỳ điều gì trong tiêu chuẩn cấm trình biên dịch từ thực hiện các điều chỉnh của 8.3.5 so với tuyên bố mẫu, và nó cũng cải thiện hiệu suất khi nói đến kết hợp quá tải.

Áp dụng điều này để ví dụ của bạn, g ++ có thể được điều trị nó như:

template<typename T> 
char (&f(T*))[1]; 

template<typename T> 
char (&f(...))[2]; 

int main() { char c[sizeof(f<void()>(0)) == 2]; } 

Ở phía trên, tham số thay là một con trỏ pháp lý để chức năng, chứ không phải là một mảng của các chức năng.

Vì vậy, câu hỏi đặt ra cho tôi là - liệu có điều gì ngăn cấm các điều chỉnh cho các tham số chức năng (8.3.5) hai lần không?

Cá nhân, tôi nghĩ rằng nó làm cho tinh thần để cho phép điều chỉnh xảy ra hai lần vì nếu không nó làm phức tạp phù hợp với chức năng template quá tải

Tóm lại, tôi nghĩ rằng giá trị của nó đối với g ++ để chọn quá tải đầu tiên dựa về cách nó xử lý các tham số mảng phân rã và Comeau sai không có lỗi khấu trừ cho mảng chức năng. Tất nhiên điều này có nghĩa là (nếu Comeau được sửa) thì mỗi trình biên dịch sẽ chọn một tình trạng quá tải khác nhau và vẫn sẽ là tiêu chuẩn tuân thủ! :(

EDIT:

Chỉ để minh họa cho quan điểm của tôi, hãy xem xét đoạn mã sau:.

template <typename T> void foo (T *); 
template <typename T> void foo (T * const); 
template <typename T> void foo (T []); 
template <typename T> void foo (T [10]); 
template <typename T> void foo (T [100]); 

void bar() 
{ 
    foo < void() > (0); 
} 

Ở đây, foo đã được tuyên bố và redeclared nhiều lần nào tuyên bố, và vì vậy mà thông số loại, trình biên dịch có áp dụng các quy tắc được liệt kê trong 14.8.2 không?

Quan điểm của tôi là tiêu chuẩn không nói bất cứ điều gì ở trên. Tôi cũng sẽ cố gắng nói rằng bất kỳ từ ngữ nào về điều này sẽ có để nó là hành vi "không xác định" hoặc "được xác định thực hiện".

+0

hmm điểm tốt về nhiều khai báo. Tôi không có ý tưởng về những gì được chọn ở đó, thực sự. Bệnh phải tìm lại sau. Cảm ơn bạn đã giúp dude của bạn :) Tôi cũng nghĩ rằng bây giờ nó sẽ là một ý tưởng tốt chỉ để nói nó decays đầu tiên (bằng cách sử dụng "T là yếu tố/loại chức năng"), sau đó thay thế, sau đó phân rã một lần nữa. –

+0

Tôi càng nghĩ về điều này, tôi càng cảm thấy rằng nó xứng đáng có một vấn đề cốt lõi mở ra cho nó. Tôi đã thử một số ví dụ với Comeau và nó có vẻ là loại chức năng đầu tiên được sử dụng để thực hiện việc kiểm tra luôn. Tuy nhiên, điều này là không hợp lý, vì nó ngụ ý rằng thứ tự mà các khai báo được nhìn thấy là quan trọng - một cái gì đó không phải là trường hợp. –

+0

Một khả năng khác là nêu rõ hai khai báo mẫu có danh sách kiểu tham số giống nhau tương đương funtionally (được định nghĩa tại 14.6.6.1/6 - cho đến nay chỉ cho các biểu thức, chưa cho các kiểu tham số). Nhưng nói rằng chúng là tương đương nếu các tham số-type-list không được sửa đổi là giống nhau. Sau đó, nếu các khai báo của một mẫu hàm không tương đương, nhưng tương đương với hàm, chương trình đó không đúng định dạng; không cần chẩn đoán. –

1

Đáng ngạc nhiên đủ - điều này không hoạt động trong VS2008. Tôi không nghĩ đó là nhất thiết phải chứng minh cho nó là hành vi đúng hay không dù ...

Visual Studio được interpretting

char (&f(T[1]))[1]; 

như một chức năng mà phải mất một mảng có kích thước 1 của T, và trả về một tham chiếu đến một loạt các ký tự có kích thước 1.

1

Tôi sẽ cố gắng mô tả quá trình khấu trừ đối số mẫu khi tôi hiểu nó khi đọc tiêu chuẩn.

  1. Đối số mẫu rõ ràng được chọn như được mô tả trong 14.8.2/2.
  2. Chữ ký hàm kết quả được điều chỉnh theo 8.3.5 (ví dụ: mảng để phân rã con trỏ được thực hiện).
  3. Đối số mẫu ngầm định được suy ra theo 14.8.2.1 (điều này được thực hiện trên chữ ký được thay thế một phần từ bước 2).

Khoản khấu trừ cho quá tải đầu tiên không thành công ở bước 1, độ phân giải quá tải do đó sẽ trả về quá tải thứ hai. Tôi không tin rằng chương trình này không được hình thành.

+1

một lỗi trừ sẽ không dẫn đến thất bại biên dịch. 14.8.3/1 nói "Nếu, đối với một mẫu chức năng nhất định, khấu trừ đối số không thành công, không có chức năng nào được thêm vào tập hợp các hàm ứng cử viên cho mẫu đó." Không nơi nào nó được tuyên bố rằng một thất bại như vậy kết quả trong một chương trình bị bệnh hình thành. –

+0

litb, bạn đúng tất nhiên. – avakar

+0

Nó là ít đáng lo ngại rằng Comeau nên được sai, mặc dù. – avakar

Các vấn đề liên quan