2016-02-10 22 views
7

Với mẫu mã này, các quy tắc liên quan đến tuổi thọ của chuỗi tạm thời được chuyển đến S là gì.Thành viên tham chiếu tổng hợp và tuổi thọ tạm thời

struct S 
{ 
    // [1] S(const std::string& str) : str_{str} {} 
    // [2] S(S&& other) : str_{std::move(other).str} {} 

    const std::string& str_; 
}; 

S a{"foo"}; // direct-initialization 

auto b = S{"bar"}; // copy-initialization with rvalue 

std::string foobar{"foobar"}; 
auto c = S{foobar}; // copy-initialization with lvalue 

const std::string& baz = "baz"; 
auto d = S{baz}; // copy-initialization with lvalue-ref to temporary 

Theo tiêu chuẩn:

N4140 12,2 p5.1 (loại bỏ trong N4296)

Một tạm thời bị ràng buộc cho một thành viên tham khảo trong ctor-initializer của một nhà xây dựng (12.6.2) vẫn tồn tại cho đến khi thoát khỏi các nhà xây dựng .

N4296 12.6.2 p8

Một biểu thức tạm thời ràng buộc với một thành viên tham chiếu trong một mem-initializer là vô hình thành.

Vì vậy, việc tạo một hàm tạo do người dùng xác định như [1] hoàn toàn không phải là điều chúng tôi muốn. Nó thậm chí còn được coi là hình thành không đúng trong C++ 14 mới nhất (hoặc là nó?) Và cả gcc lẫn clang đều không cảnh báo về nó.
Có thay đổi khi khởi tạo tổng hợp trực tiếp không? Tôi trông giống như trong trường hợp đó, tuổi thọ tạm thời được mở rộng.

Bây giờ liên quan đến việc khởi tạo sao chép, Default move constructor and reference members cho biết rằng [2] được tạo ra hoàn toàn. Với thực tế là di chuyển có thể được elided, không quy tắc tương tự áp dụng cho các nhà xây dựng di chuyển ngầm tạo ra?

Loại nào trong số a, b, c, d có tham chiếu hợp lệ?

+0

Không có ngoại lệ nào từ phần mở rộng thời gian của thời gian chờ để khởi tạo tổng hợp, do đó toàn bộ thời gian tạm thời sẽ được mở rộng. Điều này đảm bảo một đời thích hợp cho việc tạo tạm thời trong trường hợp "khởi tạo trực tiếp". – dyp

+0

ý của bạn là "di chuyển có thể được elided"? Tham chiếu ràng buộc không thể được elided, 'str_' ràng buộc trực tiếp đến' other.str'. ('std :: move' không có hiệu lực) –

+0

@ M.M Tôi có nghĩa là hầu hết các trình biên dịch sẽ thực hiện khởi tạo trực tiếp thay vì sử dụng hàm tạo di chuyển. Có 'std :: move (khác) .str' giống như' other.str' cho tham chiếu và không có hiệu lực ở đây – 3XX0

Trả lời

2

Thời gian tồn tại của các đối tượng tạm thời bị ràng buộc vào tham chiếu được kéo dài, trừ khi có một ngoại lệ cụ thể. Đó là, nếu không có ngoại lệ như vậy, sau đó cuộc đời sẽ được mở rộng.

Từ một dự thảo khá gần đây, N4567:

Bối cảnh thứ hai [nơi tuổi thọ là mở rộng] là khi một tài liệu tham khảo được ràng buộc với một tạm thời. Tạm thời mà tham chiếu là ràng buộc hoặc tạm thời mà là đối tượng hoàn toàn của một subobject mà tham chiếu được ràng buộc kéo dài tuổi thọ của tham chiếu trừ:

  • (5,1) Một đối tượng tạm thời bị ràng buộc tham số tham chiếu trong cuộc gọi hàm (5.2.2) vẫn tồn tại cho đến khi hoàn thành biểu thức đầy đủ chứa cuộc gọi.
  • (5.2) Thời gian tồn tại của một giới hạn tạm thời với giá trị trả về trong câu lệnh trả về hàm (6.6.3) không được kéo dài; tạm thời là bị hủy ở cuối biểu thức đầy đủ trong câu lệnh trả về.
  • (5.3) Một giới hạn tạm thời với tham chiếu trong bộ khởi tạo mới (5.3.4) vẫn tồn tại cho đến khi hoàn thành biểu thức đầy đủ chứa bộ khởi tạo mới.

Sự thay đổi đáng kể duy nhất để C++ 11 là, như OP đã đề cập, rằng trong C++ 11 đã có một ngoại lệ bổ sung cho dữ liệu thành viên của các loại tài liệu tham khảo (từ N3337):

  • Một giới hạn tạm thời cho một thành viên tham chiếu trong bộ khởi tạo của hàm khởi tạo (12.6.2) vẫn tồn tại cho đến khi hàm khởi tạo xuất hiện.

này đã bị xóa trong CWG 1696 (post-C++ 14), và ràng buộc đối tượng tạm thời để tham khảo các thành viên dữ liệu thông qua các mem-initializer bây giờ đã vô hình thành.


Về các ví dụ trong OP:

struct S 
{ 
    const std::string& str_; 
}; 

S a{"foo"}; // direct-initialization 

này tạo ra một tạm thời std::string và khởi tạo các thành viên str_ dữ liệu với nó. Các S a{"foo"} sử dụng tổng hợp-initialization, do đó, không có mem-initializer có liên quan. Không có ngoại lệ nào cho các tiện ích mở rộng lâu dài được áp dụng, do đó thời gian tồn tại của thời gian đó được kéo dài đến tuổi thọ của thành viên dữ liệu tham chiếu str_.


auto b = S{"bar"}; // copy-initialization with rvalue 

Trước sự bỏ bớt bản bắt buộc với C++ 17: Chính thức, chúng ta tạo ra một tạm thời std::string, khởi tạo một tạm thời S bằng cách liên kết tạm thời std::string cho thành viên str_ tài liệu tham khảo . Sau đó, chúng tôi di chuyển tạm thời S đó thành b. Thao tác này sẽ "sao chép" tham chiếu, thao tác này sẽ không kéo dài tuổi thọ của std::string tạm thời. Tuy nhiên, việc triển khai sẽ di chuyển di chuyển từ tạm thời S sang b. Tuy nhiên, điều này không ảnh hưởng đến toàn bộ thời gian tạm thời của std::string. Bạn có thể quan sát này trong chương trình sau:

#include <iostream> 

#define PRINT_FUNC() { std::cout << __PRETTY_FUNCTION__ << "\n"; } 

struct loud 
{ 
    loud() PRINT_FUNC() 
    loud(loud const&) PRINT_FUNC() 
    loud(loud&&) PRINT_FUNC() 
    ~loud() PRINT_FUNC() 
}; 

struct aggr 
{ 
    loud const& l; 
    ~aggr() PRINT_FUNC() 
}; 

int main() { 
    auto x = aggr{loud{}}; 
    std::cout << "end of main\n"; 
    (void)x; 
} 

Live demo

Lưu ý rằng destructor của loud được gọi trước khi "kết thúc của chính", trong khi x cuộc sống cho đến sau dấu vết đó. Chính thức, tạm thời loud bị hủy ở cuối biểu thức đầy đủ đã tạo ra nó.

Hành vi không thay đổi nếu hàm tạo di chuyển của aggr được người dùng xác định.

Với bắt buộc sao chép sự bỏ bớt trong C++ 17: Chúng tôi xác định các đối tượng trên RHS S{"bar"} với các đối tượng trên LHS b. Điều này làm cho toàn bộ thời gian tạm thời được kéo dài đến tuổi thọ của b. Xem CWG 1697.


Đối với hai ví dụ còn lại, hàm tạo di chuyển - nếu được gọi - chỉ cần sao chép tham chiếu. Dĩ nhiên, hàm tạo di chuyển (của S) có thể được elided, nhưng điều này không thể quan sát được vì nó chỉ sao chép tham chiếu.

+0

Có lẽ 'auto b = S {" bar "};' sẽ thay đổi cho C++ 17? Mục đích của các thay đổi đối với các giá trị là 'S s {x};' phải chính xác giống với 'auto s = S {x};', mặc dù tôi chưa kiểm tra từ ngữ chính xác sẽ bao gồm trường hợp này. –

+0

@MM Nó không phải là rất dễ dàng cho tôi để hiểu từ ngữ mới (* "biểu thức khởi tạo được sử dụng để khởi tạo đối tượng đích" *, 17.6.1) nhưng https://wg21.link/cwg1697 gợi ý rằng 'b' và 'S {" bar "}' tham chiếu đến cùng một đối tượng, do đó thời gian tồn tại tạm thời nên được kéo dài đến thời gian tồn tại của 'b'. Tôi không nghĩ rằng các trình biên dịch hiện đang thực hiện điều này, mặc dù (xem bản demo Live của câu trả lời). – dyp

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