2012-12-31 44 views
6

Mã này thất bại trong việc biên dịch ở hầu hết các trình biên dịch nhưng lúc đầu tôi bằng trực giác mong đợi SFINAE để bảo vệ tôi:SFINAE, khấu trừ vs instantiation

typedef void (*A)(); 

template < typename T > 
struct a_metafun { typedef typename T::type type; }; 

template < typename T > 
typename a_metafun<T>::type f(T) {} 

template < typename T> 
void f(T(*)()) {} 

int main() { f(A()); } 

tôi có thể sửa chữa các vấn đề trong ít nhất hai cách:

1) Thay đổi định nghĩa về "metafun" f() tới:

template < typename T > typename T::type f(T) {}

2) định nghĩa "a_metafun" như vậy mà nó phân tích T và có một loại nếu T có một và không làm tôi f nó không ... nhưng instantiates mà không có lỗi một trong hai cách:

BOOST_MPL_HAS_XXX_TRAIT_DEF(type) 

typedef < template T, bool = has_type<T>::value > 
struct a_metafun { }; 

typedef < template T > 
struct a_metafun<T, true> { typedef typename T::type type }; 

Khi nhìn vào 14.8.2 (C++ 03) có vẻ như tôi chỉ định chính xác trong điều kiện nào mà SFINAE có thể áp dụng. Có một nơi nào tốt hơn để xem không? Thất bại trong quá trình khởi tạo của một mẫu đã được rút ra, ngay cả khi khấu trừ một mẫu khác, dường như không được đưa vào danh sách này.

Một hướng khác mà tôi đã thực hiện để giải thích điều gì làm cho điều này bất hợp pháp là việc khấu trừ a_metafun đã diễn ra và sự khởi tạo của nội bộ của nó là nguyên nhân gây ra lỗi. SFINAE không áp dụng trong quá trình khởi tạo nhưng chỉ trong khi khấu trừ, hoặc tôi có sai ở đó không? Tuy nhiên, trong trường hợp thứ hai, a_metafun đang được chính xác, và được khởi tạo tốt nhưng nó đơn giản không có định nghĩa "type" bên trong, có nghĩa là mẫu cố gắng khởi tạo nó không thành công do thay thế.

Về cơ bản, tôi tự hỏi điều gì trong tiêu chuẩn xác định hành vi mà tôi đang chứng kiến. Mỗi trình biên dịch tôi đã thử phàn nàn, thậm chí là comeau. Tôi có ý kiến ​​rằng họ đúng khi làm như vậy, tôi không hoàn toàn chắc chắn là tại sao.

Vì vậy, các chuyên gia ... là gì? Tại sao việc khởi tạo loại, ngay cả trong bối cảnh khấu trừ trong f() gây ra lỗi hơn là loại trừ SFINAE?

+0

Tôi nghĩ rằng nên thất bại trong C++ 11, chứ không phải trong C++ 03 mặc dù. Quy tắc SFINAE (hay đúng hơn là * từ *) được thay đổi một chút trong C++ 11. – Nawaz

Trả lời

2

SFINAE sẽ không bảo vệ bạn ở đó, lỗi xảy ra sau khi khấu trừ loại. Tuy nhiên, điều này sẽ làm việc:

template < typename T, typename Type = typename T::type > 
struct a_metafun { typedef Type type; }; 

By accesing T::type trong một mẫu mặc định tham số chúng ta gây ra điều này xảy ra vào thời điểm thay thế, và tại thời điểm đó SFINAE đá trong

Edit:. Sau khi suy nghĩ một số chi tiết, tôi không chắc chắn lý do tại sao triển khai hiện tại của bạn không thành công. Tôi nghĩ là vì a_metafun loại thành viên type, một nguyên nhân gây ra lỗi biên dịch; nó sẽ khác nếu a_metafun không có kiểu thành viên type.

+0

Xem câu trả lời của tôi tại sao nó không thành công trong C++ 11. – Nawaz

4

Trong đặc tả C++ 03, quy tắc của SFINAE hơi mơ hồ, cho phép tác giả trình biên dịch truy cập bất kỳ độ dài nào để tìm lỗi thay thế dẫn đến SFINAE. Văn bản liên quan §14.8.2/2 từ C++ 03 cho biết,

- [...] Nếu thay thế một tham số mẫu hoặc loại chức năng của mẫu chức năng dẫn đến loại không hợp lệ, loại khấu trừ không thành công [...]

Nó giải thích thêm một vài lý do cho sự thất bại, nhưng không ai trong số họ thực sự nói tại điểm nào thay thế lỗi nên được coi là SFINAE. Vì vậy, tôi đoán, mã của bạn có thể làm việc tốt trong C + + 03 (hoặc có thể không, tùy thuộc vào cách các tác giả biên dịch giải thích văn bản. Nó là khó hiểu với tôi anyway).

Nhưng các từ trong C++ 11 đã được cải thiện để loại bỏ sự mơ hồ. Nó nói trong §14.8.2/8,

Nếu thay thế dẫn đến loại hoặc biểu thức không hợp lệ, loại khấu trừ sẽ không thành công. Một loại hoặc biểu thức không hợp lệ là loại hoặc biểu thức không hợp lệ nếu được viết bằng các đối số được thay thế. [Lưu ý: Kiểm tra truy cập được thực hiện như là một phần của quá trình thay thế. —thêm ghi chú] Chỉ các kiểu và biểu thức không hợp lệ trong ngữ cảnh ngay lập tức của loại hàm và kiểu tham số mẫu của nó có thể dẫn đến lỗi khấu trừ.

Thuật ngữ "bối cảnh ngay lập tức" là thú vị, và tôi nghĩ rằng nó áp dụng cho tình hình của bạn. Cụ thể hơn, lỗi thay thế trong hàm meta a_metafun không được coi là "ngữ cảnh ngay lập tức" của loại hàm. Nó không được hình thành trong C++ 11, chứ không phải SFINAE.

Định nghĩa "ngữ cảnh ngay lập tức" không đủ rõ ràng trong C++ 11. Dưới đây là một vấn đề đang hoạt động:

+1

Thật thú vị! Tôi không biết về thay đổi này trong _SFINAE_. Cảm ơn bạn –

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