Tại sao lớp của tôi không mặc định-xây dựng?


28

Tôi có những lớp học đó:

#include <type_traits>

template <typename T>
class A {
public:
    static_assert(std::is_default_constructible_v<T>);

};

struct B {
   struct C {
      int i = 0;
   };

    A<C> a_m;
};

int main() {
    A<B::C> a;
}

Khi biên dịch, a_mkhông thể xây dựng mặc định nhưng alà.

Khi đổi Csang:

struct C {
      int i;
   };

mọi thứ đều ổn.

Đã thử nghiệm với Clang 9.0.0.


3
GCC 8.3 - OK, GCC 9.1 / 9.2 - Không thành công.
Evg

2
Với C() {}nó hoạt động, quá.
Evg

3
Điều này có mùi lỗi với tôi. Không có trận đấu rõ ràng ngay lập tức trên Bugzilla.
Các cuộc đua nhẹ nhàng trong quỹ đạo

2
Thú vị: static_asserttrong Athất bại, nhưng nếu bạn mặc định xây dựng Tbên trong A(ví dụ: đặt một thành viên T t;ở đó), nó hoạt động tốt. Sự không nhất quán giữa những đặc điểm loại đang nói với bạn và những gì thực sự có thể ...
sebrockm

2
@Nicolas Đúng, nhưng đó là do một số trường hợp cạnh, không có trường hợp nào áp dụng ở đây (đặc biệt, như cùng một câu trên cppreference nói, const int x;không hợp lệ nếu không có trình khởi tạo, hoàn toàn do consthành vi khởi tạo của các loại tích hợp và một số lịch sử)
Các cuộc đua nhẹ nhàng trong quỹ đạo

Câu trả lời:


9

Điều này không được phép bởi cả văn bản của tiêu chuẩn và một số triển khai chính như đã nêu trong các ý kiến, nhưng vì những lý do hoàn toàn không liên quan.

Đầu tiên, lý do "theo sách": điểm khởi tạo A<C>là, theo tiêu chuẩn, ngay trước định nghĩaB và điểm khởi tạo std::is_default_constructible<C>là ngay trước đó:

Đối với chuyên môn hóa mẫu lớp, [...] nếu chuyên môn hóa được khởi tạo hoàn toàn bởi vì nó được tham chiếu từ bên trong một chuyên môn mẫu khác, nếu bối cảnh mà chuyên môn hóa được tham chiếu phụ thuộc vào một tham số mẫu và nếu việc chuyên môn hóa không được khởi tạo trước đó đến phần khởi tạo của mẫu kèm theo, điểm khởi tạo ngay trước điểm khởi tạo của mẫu kèm theo. Mặt khác, điểm khởi tạo cho một chuyên môn như vậy ngay lập tức đi trước khai báo hoặc định nghĩa phạm vi không gian tên đề cập đến chuyên môn hóa.

Crõ ràng là không đầy đủ tại thời điểm đó, hành vi khởi tạo std::is_default_constructible<C>không được xác định. Tuy nhiên, xem vấn đề cốt lõi 287 , sẽ thay đổi quy tắc này.


Trong thực tế, điều này có liên quan đến NSDMI.

  • Các NSDMI rất kỳ lạ vì chúng bị phân tích cú pháp bị trì hoãn - hoặc theo cách nói chuẩn, chúng là một "bối cảnh hoàn chỉnh của lớp".
  • Do đó, = 0về nguyên tắc , điều đó có thể đề cập đến những điều Bchưa được khai báo, vì vậy việc triển khai thực sự không thể cố gắng phân tích nó cho đến khi nó kết thúc B.
  • Việc hoàn thành một lớp đòi hỏi phải khai báo ngầm định các hàm thành viên đặc biệt, đặc biệt là hàm tạo mặc định, vì Ckhông có hàm tạo được khai báo.
  • Các phần của tuyên bố đó (constexpr-ness, noexcept-ness) phụ thuộc vào các thuộc tính của NSDMI.
  • Do đó, nếu trình biên dịch không thể phân tích cú pháp NSDMI, nó không thể hoàn thành lớp.
  • Kết quả là, tại thời điểm nó bắt đầu A<C>, nó nghĩ rằng nó Ckhông đầy đủ.

Toàn bộ khu vực này liên quan đến các khu vực bị phân tích cú pháp chậm được xác định rõ ràng, với sự phân kỳ thực hiện đi kèm. Nó có thể mất một lúc trước khi nó được làm sạch.


0

Hành vi không xác định đó là:

Nếu việc khởi tạo một mẫu ở trên phụ thuộc trực tiếp hoặc gián tiếp vào một loại không hoàn chỉnh và việc khởi tạo đó có thể mang lại một kết quả khác nếu loại đó được hoàn thành theo giả thuyết, hành vi không được xác định.


7
Tại sao C không đầy đủ?
interjay

1
@interjay, Cđã hoàn tất, nhưng Bkhông phải. Và B::Cphụ thuộc gián tiếp vào B.
Evg

1
@Evg Văn bản "Phụ thuộc trực tiếp hoặc gián tiếp" chỉ xuất hiện trên cppreference.com. Tiêu chuẩn chỉ nói rằng loại T cần phải được hoàn thành.
interjay


2
@interjay Mình viết phần lớn từ này. Điều chúng tôi đang cố gắng nói là 1) nếu bạn khởi tạo một đặc điểm theo cách có thể gây ra vi phạm ODR sau này khi một số loại không hoàn chỉnh được hoàn thành, điều đó không được xác định; và 2) không xác định được ngay cả khi bạn không thực sự gây ra vi phạm ODR trong chương trình của mình, do đó, việc triển khai thư viện tiêu chuẩn có thể chọn để chẩn đoán rằng tại thời điểm đó, đặc điểm được sử dụng nếu họ mong muốn. Nếu Ccó một mẫu xây dựng mặc định với một số SFINAE kỳ lạ có thể thay đổi câu trả lời nếu Bđược hoàn thành khác nhau, thì chắc chắn, đặc điểm phụ thuộc vào nó.
TC
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.