Cố gắng hiểu mẫu và tra cứu tên


9

Tôi đang cố gắng để hiểu các đoạn mã sau

Đoạn trích số 1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

Cả gcc9 và clang9 đều không có lỗi ở đây.

Q. Tại sao mã này biên dịch? Không phải chúng ta đang bắt đầuA<B> khi thừa kế từ B sao? Không có VD trong B, vậy trình biên dịch có nên đưa ra lỗi ở đây không?

Đoạn trích số 2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

Trong trường hợp này, gcc9 biên dịch tốt nhưng clang9 đưa ra lỗi thông báo "Không có thành viên nào tên AD trong B".

Q. Tại sao nó biên dịch với gcc9 / tại sao nó không biên dịch với clang9?

Đoạn số 3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

Ở đây cả clang9 và gcc9 đều gặp lỗi. gcc9 nói "sử dụng không hợp lệ loại không hoàn chỉnh 'struct B'".

Q. Nếu cấu trúc B không đầy đủ ở đây thì tại sao nó không hoàn chỉnh trong đoạn số 2?

Cờ trình biên dịch được sử dụng : -std=c++17 -O3 -Wall -Werror. Cảm ơn trước!!!


@xception là không struct Binstantiating Avới B?
Tác dụng phụ có thể thay đổi

clang9 đưa ra một lỗi thông báo "Không có thành viên tên AD trong B" . như Bchưa hoàn thành ... Nhưng không chắc chắn khi nào thành viên sẽ được khởi tạo ..
Jarod42 16/12/19

@MutableSideEffect oh vâng, xấu của tôi, hãy đọc nó dưới dạng mẫu :(
xception

@ Jarod42 thì tại sao gcc biên dịch tốt?
Tác dụng phụ có thể thay đổi

1
Tôi đã gắn cờ câu hỏi này là "cần tập trung hơn" và câu hỏi thực sự chứa nhiều hơn một câu hỏi (do đó là kết luận của tôi), vậy tại sao cờ của tôi sai?
Dominique

Câu trả lời:


4

Tôi tin rằng những thứ này về cơ bản sôi xuống [temp.inst] / 2 (nhấn mạnh của tôi):

Việc khởi tạo ngầm định của chuyên môn hóa mẫu lớp gây ra việc khởi tạo ngầm định các khai báo, nhưng không phải là các định nghĩa , đối số mặc định hoặc các chỉ định noexcept của các hàm thành viên lớp, các lớp thành viên, liệt kê thành viên có phạm vi, các thành viên dữ liệu tĩnh , các mẫu thành viên và bạn bè; [Càng]

[temp.inst] / 9

Việc triển khai sẽ không được khởi tạo ngầm [[]] một thành viên dữ liệu tĩnh của mẫu lớp [trừ] trừ khi việc khởi tạo đó là bắt buộc.

Các từ ngữ trong tiêu chuẩn liên quan đến khởi tạo mẫu ẩn để lại nhiều chi tiết mở cho việc giải thích. Nói chung, dường như với tôi rằng bạn chỉ đơn giản là không thể dựa vào các phần của mẫu không được khởi tạo trừ khi thông số kỹ thuật nói rõ ràng như vậy. Như vậy:

Đoạn trích số 1

Q. Tại sao mã này biên dịch? Không phải chúng ta bắt đầu A khi thừa kế từ B sao? Không có VD trong B, vậy trình biên dịch có nên đưa ra lỗi ở đây không?

Bạn đang bắt đầu A<B>. Nhưng khởi tạo A<B>chỉ khởi tạo các khai báo, không phải định nghĩa của các thành viên dữ liệu tĩnh của nó. VBkhông bao giờ được sử dụng theo cách đòi hỏi một định nghĩa để tồn tại. Trình biên dịch nên chấp nhận mã này.

Đoạn trích số 2

Q. Tại sao nó biên dịch với gcc9 / tại sao nó không biên dịch với clang9?

Như Jarod42 đã chỉ ra, tuyên bố ABchứa loại giữ chỗ. Dường như với tôi rằng từ ngữ của tiêu chuẩn không thực sự rõ ràng về những gì sẽ xảy ra ở đây. Việc khởi tạo khai báo của một thành viên dữ liệu tĩnh có chứa khấu trừ loại giữ chỗ kích hoạt loại giữ chỗ và do đó, tạo thành một sử dụng đòi hỏi định nghĩa của thành viên dữ liệu tĩnh? Tôi không thể tìm thấy từ ngữ trong tiêu chuẩn có thể nói rõ ràng có hoặc không với điều đó. Vì vậy, tôi muốn nói rằng cả hai cách giải thích đều có giá trị như nhau ở đây và do đó, GCC và clang đều đúng

Đoạn số 3

Q. Nếu cấu trúc B không đầy đủ ở đây thì tại sao nó không hoàn chỉnh trong đoạn số 2?

Một loại lớp chỉ hoàn thành tại điểm mà bạn đạt đến kết thúc }của trình xác định lớp [class.mem] / 6 . Do đó, Bkhông đầy đủ trong quá trình khởi tạo ngầm A<B>trong tất cả các đoạn của bạn. Chỉ là điều này không liên quan đến Snippet # 1. Trong Snippet # 2, No member named AD in Bkết quả là clang đã gây ra lỗi cho bạn . Tương tự như trường hợp của Snippet # 2, tôi không thể tìm thấy từ ngữ khi chính xác các khai báo bí danh thành viên sẽ được khởi tạo. Tuy nhiên, không giống như định nghĩa của các thành viên dữ liệu tĩnh, không có từ ngữ nào để ngăn chặn rõ ràng việc khởi tạo các khai báo bí danh thành viên trong quá trình khởi tạo ngầm định của mẫu lớp. Vì vậy, tôi sẽ nói rằng hành vi của cả GCC và clang là một cách giải thích hợp lệ của tiêu chuẩn trong trường hợp này


Cảm ơn. Trong trường hợp này, chúng tôi khởi tạo thành viên dữ liệu tĩnh trong cơ thể. Là phần khởi tạo của khai báo, hay một phần định nghĩa của thành viên dữ liệu tĩnh? Tôi có ấn tượng rằng nếu việc khởi tạo nằm trong cơ thể, thì đó là một phần của tuyên bố của thành viên dữ liệu tĩnh. Nếu đó là một phần của khai báo, thì trích dẫn đầu tiên của bạn yêu cầu khởi tạo ngay lập tức như là một phần của việc khởi tạo ngầm xung quanh mẫu lớp.
Julian Schaub - litb

Tôi đã xem xét thông số kỹ thuật và có vẻ như có sự khác biệt giữa C ++ 14 và C ++ 17 ở đây. Trong C ++ 14, constexprthành viên dữ liệu tĩnh chỉ là một khai báo. C ++ 17 đã đạt được inlinecác biến và constexprhàm ý inline, và điều này làm cho khai báo thành viên dữ liệu tĩnh trong cơ thể thành một định nghĩa.
Julian Schaub - litb

eel.is/c++draft/dcl.spec.auto#4.sentence-2 nói "Loại biến được khai báo sử dụng loại giữ chỗ được suy ra từ trình khởi tạo của nó. Việc sử dụng này được cho phép trong khai báo khởi tạo ([dcl. init]) của một biến. ". Do đó, tôi cho rằng định nghĩa của thành viên dữ liệu tĩnh là bắt buộc, bởi vì nó chứa trình khởi tạo.
Julian Schaub - litb

@ JohannesSchaub-litb Cảm ơn bạn đã xem xét điều này! Tôi cũng đã tự hỏi về những câu hỏi này nhưng không thể tìm thấy bất kỳ từ ngữ nào có thể kết luận được. Liên quan đến việc khởi tạo so với điều định nghĩa, hãy xem xét định nghĩa của hàm thành viên bên trong định nghĩa của mẫu lớp. Một định nghĩa như vậy cũng là một tuyên bố và không có tuyên bố khác. Tuy nhiên, việc khởi tạo ngầm định của mẫu lớp sẽ chỉ khởi tạo một khai báo chứ không phải định nghĩa của hàm thành viên. Tại sao điều tương tự không đúng với các thành viên dữ liệu tĩnh?
Michael Kenzel

nếu không có gì đòi hỏi definiton, định nghĩa không được khởi tạo. Nhưng trong autotrường hợp, quy tắc nói rằng khai báo phải là một khai báo khởi tạo. Đây chỉ có thể là trường hợp nếu biết rằng tuyên bố là một định nghĩa (theo như tôi biết .. Tôi đã rời khỏi vùng đất luật sư trong một thời gian). Trước đây, có một trường hợp tương tự và eel.is/c++draft/temp.inst#2.sentence-3 đã được thêm vào, trong đó một tuyên bố rằng một định nghĩa được khởi tạo là "được biết là một định nghĩa" mà không thực sự khởi tạo định nghĩa.
Julian Schaub - litb
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.