2012-04-29 5 views
8

Khi nào là các hàm thành viên đặc biệt (cụ thể, sao chép/di chuyển các nhà xây dựng và sao chép/di chuyển các toán tử gán) của một lớp mẫu được khởi tạo? Ngay khi bản thân lớp được khởi tạo, hoặc chỉ khi chúng cần thiết?Khi nào các hàm thành viên đặc biệt của một lớp mẫu được khởi tạo?

này đi lên trong tình huống sau đây:

template <class T, class U> 
struct pair 
{ 
    T first;     
    U second;     

    pair() : first(), second() {} 

    pair(const pair&) = default; 
}; 

struct S 
{ 
    S() {} 
    S(const S&) = delete; 
    S(S&&) = default; 
}; 

int main() 
{ 
    pair<int, S> p; 
} 

Clang từ chối để biên dịch mã này, với các lỗi sau đây:

test.cpp:9:5: error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be 
     non-const 
    pair(const pair&) = default; 
    ^
test.cpp:21:18: note: in instantiation of template class 'pair<int, S>' requested here 
    pair<int, S> p; 
       ^

gợi ý rằng nó sẽ cố gắng để nhanh chóng các constructor sao chép càng sớm càng lớp được khởi tạo.

GCC, tuy nhiên, biên dịch mã chỉ tốt, cho thấy rằng nó sẽ chỉ cố gắng để khởi tạo các nhà xây dựng bản sao nếu nó thực sự cần thiết.

Hành vi của trình biên dịch nào là chính xác?

(. Một sự khác biệt tương tự được trưng bày cho toán tử gán)

CẬP NHẬT: Đây có gì để làm với thực tế là các nhà xây dựng bản sao của pair trong ví dụ này là default ed, bởi vì nếu tôi thay đổi định nghĩa của nó đến số

pair(const pair& p) : first(p.first), second(p.second) {} 

thì mã cũng được chuyển.

+0

Biên dịch tốt bằng clang3.0 với -std = C++ 11. –

Trả lời

2

Các đoạn có liên quan của tiêu chuẩn là [dcl.fct.def.default]/1:

Một chức năng mà được mặc định một cách rõ ràng trong phạm vi nhiệm [...] có cùng một loại chức năng khai báo (ngoại trừ trường hợp có thể khác nhau và ngoại trừ trường hợp của một nhà xây dựng sao chép hoặc nhà điều hành gán bản sao, kiểu tham số có thể là "tham chiếu đến non-const T", trong đó T là tên của lớp của hàm thành viên) đã được khai báo hoàn toàn

Quy tắc này áp dụng ngay cả khi chức năng mặc định không bao giờ được sử dụng. Bây giờ, [class.copy]/9 nói:

Các constructor sao chép ngầm-tuyên bố sẽ có dạng

X::X(const X&)

nếu [...] cho tất cả các thành viên dữ liệu không tĩnh của X thuộc loại lớp M [...], mỗi loại lớp như vậy có một hàm tạo bản sao có thông số đầu tiên là loại const M& hoặc const volatile M&.

Nếu không copy constructor ngầm-tuyên bố sẽ có dạng

X::X(X&)

Do đó, một ví dụ như thế này là vô hình thành (và nên tạo ra chẩn đoán bạn đang nhìn thấy):

struct A { 
    A(); 
    A(A&); // Note, non-const type A in copy constructor 
}; 
template<typename T> 
struct B { 
    T t; 
    B(); 
    B(const B&) = default; 
}; 
B<A> b; // error, B<A>::B(const B&) is defaulted but has the wrong type 

Tuy nhiên, trong ví dụ của bạn, quy tắc này không áp dụng. Do lỗi clang (đã được sửa), các nhà xây dựng sao chép đã bị xóa không được xem là có các tham số không phải là const, dẫn đến lỗi này.

5

Hãy xem phần 14.7.1 của tiêu chuẩn C++ 11 hiện tại. Trích dẫn từ phiên bản n3242 của dự thảo:

Các instantiation tiềm ẩn của một lớp mẫu chuyên môn gây instantiation ngầm của tờ khai, nhưng không phải của định nghĩa hoặc đối số mặc định, các hàm thành viên lớp, các lớp thành viên, các thành viên dữ liệu tĩnh và các mẫu thành viên; và nó gây ra sự diễn giải ngầm định của các định nghĩa của thành viên các tổ chức ẩn danh. Trừ khi một thành viên của mẫu lớp hoặc thành viên mẫu được thể hiện rõ ràng hoặc chuyên biệt rõ ràng, chuyên môn của thành viên được khởi tạo ngầm khi chuyên môn được tham chiếu trong ngữ cảnh yêu cầu định nghĩa thành viên ; cụ thể, việc khởi tạo (và bất kỳ hiệu ứng phụ liên quan) của thành viên dữ liệu tĩnh không xảy ra trừ khi thành viên dữ liệu tĩnh được sử dụng theo cách yêu cầu định nghĩa của thành viên dữ liệu tĩnh tồn tại.

Vì vậy, điều này có nghĩa là, khi bạn sử dụng lớp như một loại như trên chỉ khai báo được khởi tạo với nó. Vì vậy, thực tế (mặc định) thực hiện của các nhà xây dựng bản sao không nên được instantiated, vì nó không cần thiết trong mã trên. Vì vậy, GCC đang xử lý chính xác, trong khi Clang thì không. Ngoài ra, bản chỉnh sửa của bạn cho thấy rằng Clang đang tạo cài đặt cho trình tạo bản sao mặc định quá sớm, vì trình tạo bản sao được triển khai trực tiếp của bạn cũng bị lỗi (bạn không thể gọi hàm tạo bản sao cho S như bạn đang thực hiện).Kể từ khi thực hiện mặc định và thực hiện của bạn nên giống nhau trong tất cả các khía cạnh (bao gồm cả thời gian instantiation), tôi sẽ xem xét điều này một lỗi clang.