Định nghĩa ngoài lớp C ++ 20 trong một lớp mẫu


12

Cho đến tiêu chuẩn C ++ 20 của C ++, khi chúng tôi muốn xác định toán tử ngoài lớp sử dụng một số thành viên riêng của lớp mẫu, chúng tôi sẽ sử dụng cấu trúc tương tự như sau:

template <typename T>
class Foo;

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);

private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

int main() {
    return 1 == Foo<int>(1) ? 0 : 1;
}

Tuy nhiên, vì C ++ 20, chúng ta có thể bỏ qua khai báo ngoài lớp, do đó cũng là khai báo chuyển tiếp, vì vậy chúng ta có thể thoát khỏi chỉ với:

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

Demo

Bây giờ, câu hỏi của tôi là, phần nào của C ++ 20 cho phép chúng ta làm như vậy? Và tại sao điều này không thể xảy ra trong các tiêu chuẩn C ++ trước đó?


Như đã chỉ ra trong các bình luận, clang không chấp nhận mã này được trình bày trong bản demo, điều này cho thấy đây thực sự có thể là một lỗi trong gcc.

Tôi đã nộp báo cáo lỗi về bugzilla của gcc


2
Cá nhân tôi thích định nghĩa lớp hơn, tránh chức năng khuôn mẫu (và khấu trừ "các vấn đề" (Không khớp với "c string" == Foo<std::string>("foo"))).
Jarod42

@ Jarod42 Tôi hoàn toàn đồng ý, tôi cũng thích định nghĩa trong lớp. Tôi đã rất ngạc nhiên khi phát hiện ra rằng C ++ 20 cho phép chúng ta không lặp lại chữ ký hàm ba lần khi định nghĩa nó là lớp, có thể hữu ích trong một API công khai, nơi triển khai trong một tệp .inl ẩn.
ProXicT

Tôi đã không nhận thấy nó là không thể. Tại sao tôi đã sử dụng nó cho đến nay mà không có vấn đề?
ALX23z

1
Hmmm, trong temp.friend , không có nhiều thay đổi, đặc biệt là 1.3 không chịu trách nhiệm cho hành vi này. Vì clang không chấp nhận mã của bạn, tôi nghiêng về gcc có lỗi.
n314159

@ ALX23z Nó hoạt động mà không cần khai báo ngoài lớp nếu lớp không được tạo mẫu.
ProXicT

Câu trả lời:


2

GCC có một lỗi.

Tra cứu tên luôn được thực hiện cho các tên mẫu xuất hiện trước một <, ngay cả khi tên trong câu hỏi là tên được khai báo trong một tuyên bố (bạn bè, chuyên môn rõ ràng hoặc khởi tạo rõ ràng).

Vì tên operator==trong khai báo kết bạn là tên không đủ tiêu chuẩn và phải tuân theo tra cứu tên trong mẫu, nên áp dụng quy tắc tra cứu tên hai pha. Trong ngữ cảnh này, operator==không phải là một tên phụ thuộc (nó không phải là một phần của lệnh gọi hàm, vì vậy ADL không được áp dụng), vì vậy tên này được tra cứu và ràng buộc tại điểm xuất hiện (xem [temp.nondep] đoạn 1). Ví dụ của bạn không đúng định dạng vì việc tra cứu tên này không tìm thấy khai báo nào operator==.

Tôi hy vọng GCC sẽ chấp nhận điều này trong chế độ C ++ 20 do P0846R0 , cho phép (ví dụ) operator==<T>(a, b)được sử dụng trong một mẫu ngay cả khi không operator==nhìn thấy tuyên bố trước nào dưới dạng mẫu.

Đây là một thử nghiệm thú vị hơn nữa:

template <typename T> struct Foo;

#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif

template <typename T> struct Foo {
  friend bool operator==<T>(Foo<T> lhs, float); // #2
};

template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;

Với -DWRONG_DECL, GCC và Clang đồng ý rằng chương trình này không được định dạng: tra cứu không đủ điều kiện cho tuyên bố kết bạn số 2, trong bối cảnh định nghĩa mẫu, tìm thấy tuyên bố # 1, không khớp với người bạn tức thời của Foo<int>. Tuyên bố # 3 thậm chí không được xem xét, bởi vì tra cứu không đủ tiêu chuẩn trong mẫu không tìm thấy nó.

Với -UWRONG_DECL, GCC (trong C ++ 17 trở về trước) và Clang đồng ý rằng chương trình này không được định hình vì một lý do khác: tìm kiếm không đủ tiêu chuẩn cho operator==dòng # 2 không tìm thấy gì.

Nhưng với -UWRONG_DECL, GCC ở chế độ C ++ 20 dường như quyết định rằng việc tìm kiếm không đủ điều kiện operator==trong # 2 không thành công (có lẽ là do P0846R0), và sau đó xuất hiện để tìm kiếm lại từ bối cảnh khởi tạo mẫu, hiện đang tìm thấy # 3, trong vi phạm quy tắc tra cứu tên hai pha thông thường cho các mẫu.


Cảm ơn cho lời giải thích chi tiết này, rất tốt đặt!
ProXicT
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.