2015-04-25 11 views
30

Tôi đang chuyển một mã C++ 14- constexpr từ Clang sang g ++ - 5.1 mới nhất. Xem xét giảm đoạn mã sau đây của một bitset lớp cây nhà lá vườn mà đã được biên soạn một cách chính xác kể từ lễ nhậm của Clang 3,3 (gần 2 năm nay!)Constexpr không được phép khai báo chuyên môn về mẫu bạn bè?

#include <cstddef> 

template<std::size_t> 
class bitset; 

template<std::size_t N> 
constexpr bool operator==(const bitset<N>& lhs, const bitset<N>& rhs) noexcept; 

template<std::size_t N> 
class bitset 
{ 
    friend constexpr bool operator== <>(const bitset<N>&, const bitset<N>&) noexcept; 
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <-- error from this piece 
}; 

template<std::size_t N> 
constexpr bool operator==(const bitset<N>& /* lhs */, const bitset<N>& /* rhs */) noexcept 
{ 
    return true; 
} 

int main() {} 

Live example trên Wandbox. Tuy nhiên, g ++ - 5.1 và phiên bản thân hiện nay đưa ra một lỗi:

'constexpr' is not allowed in declaration of friend template specialization

Câu hỏi: đây là một g ++ lỗi được biết hoặc được Clang không phù hợp với các tiêu chuẩn mới nhất?

Note: ở trên chỉ sử dụng C++ 11 phong cách constexpr tính năng, vì không có những thay đổi đang diễn ra bên trong operator==, vì vậy có vẻ như một số can thiệp lạ giữa các mẫu, bạn bè và constexpr.

CẬP NHẬT: được gửi dưới dạng bug 65977 trên Bugzilla.

+0

Và 4.8.2 phàn nàn về [một thông số nội tuyến 'không liên quan'] (http://melpon.org/wandbox/permlink/ZNUj29hUVVn5RgJr) ... – Columbo

+0

Tôi không thấy bất kỳ câu trả lời rõ ràng nào cho điều này, có thể là hữu ích để mở một báo cáo lỗi gcc, đặc biệt là do sự khác biệt của chúng được thực hiện giữa clang và gcc. Tôi không thấy bất kỳ khiếm khuyết nào liên quan đến điều này và mặc dù câu trả lời của Marco có lẽ đúng, nhưng nó không rõ ràng là đúng với tôi. –

+0

Cá nhân tôi đã để lại tiền thưởng tại chỗ để câu trả lời có thể đã nhận được nhiều phiếu bầu hơn. Đó là một câu trả lời hoàn hảo và xứng đáng hơn 2 phiếu bầu. –

Trả lời

31

GCC không đúng ở đây.

Tất cả các tham chiếu đến N4431, C++ WD mới nhất.

[tl; dr: Có sự khác biệt giữa một chức năng là inline (hay chính xác hơn, là một chức năng inline, theo quy định tại 7.1.2/2) và được tuyên bố với inline specifier. Công cụ chỉ định constexpr làm cho nội dòng hoạt động, nhưng không phải là một thông số inline.]

Trình chỉ định được mô tả trong mục 7.1 của tiêu chuẩn C++ và là một phần của ngữ pháp. Vì vậy, bất cứ khi nào các cuộc đàm phán chuẩn về một thông số foo xuất hiện ở đâu đó, nó có nghĩa là specifier đã xuất hiện trong mã nguồn (phân tích cú pháp của). Trình chỉ định inline là một thông số chức năng , được mô tả trong mục 7.1.2 và tác dụng của nó là tạo hàm là hàm nội tuyến. (7.1.2)/2:

A function declaration (8.3.5, 9.3, 11.3) with an inline specifier declares an inline function.

Có hai cách khác để khai báo hàm nội tuyến mà không cần sử dụng mã thông số inline. Một được mô tả trong (7.1.2)/3:

A function defined within a class definition is an inline function.

Các khác được mô tả trong (7.1.5)/1:

constexpr functions and constexpr constructors are implicitly inline (7.1.2).

Không những nói rằng hành vi này là như thể một inlinechỉ định đã có mặt, chỉ đơn thuần là hàm là hàm nội tuyến.

Vậy tại sao quy tắc này tồn tại?

Có dạng đơn giản hơn của quy tắc này trong (7.1.2)/3:

If the inline specifier is used in a friend declaration, that declaration shall be a definition or the function shall have previously been declared inline.

Mục đích của việc này là để cho phép khai báo bạn để được bỏ qua trong nhiều trường hợp - họ không được phép để thêm "thông tin mới" để các thực thể kết bạn, ngoại trừ trong trường hợp đặc biệt họ đang định nghĩa một hàm bạn bè. (Điều này sẽ cho phép một thực hiện để trì hoãn phân tích một định nghĩa lớp cho đến khi nó "cần thiết".) Vì vậy, chúng tôi cũng nhận thấy, trong (8.3.6)/4:

If a friend declaration specifies a default argument expression, that declaration shall be a definition and shall be the only declaration of the function or function template in the translation unit.

Và cùng áp dụng cho một tuyên bố của một chuyên môn hóa bạn bè của một mẫu hàm: nếu nó có thể thêm thông tin bổ sung, thì việc triển khai không thể trì hoãn phân tích cú pháp định nghĩa lớp.

Bây giờ, lưu ý rằng lý do này không không áp dụng đối với constexpr: nếu constexpr specifier xuất hiện trên bất kỳ tuyên bố của một hàm, nó phải xuất hiện trên mỗi khai, mỗi (7.1.5)/1. Vì không có "thông tin mới" ở đây nên không cần giới hạn.

+1

Câu trả lời tuyệt vời, cảm ơn! Tôi sẽ gửi một báo cáo lỗi gcc (3 thực sự, kể từ thư viện DR 2275 và 2301, liên quan đến chức năng tuple constexpr, cũng chưa được thực hiện trong libstdC++) – TemplateRex

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