Có thể trả về giá trị của đối số mặc định bằng tham chiếu const không?


26

Bạn có thể trả về giá trị của đối số mặc định bằng tham chiếu const như trong các ví dụ bên dưới không:

https://coliru.stacked-crooking.com/a/ff76e060a007723b

#include <string>

const std::string& foo(const std::string& s = std::string(""))
{
    return s;
}

int main()
{
    const std::string& s1 = foo();
    std::string s2 = foo();

    const std::string& s3 = foo("s");
    std::string s4 = foo("s");
}

3
Kiểm tra đơn giản: thay thế std::stringbằng một lớp của riêng bạn để bạn có thể theo dõi xây dựng và phá hủy.
dùng4581602

1
@ user4581602 Nếu trình tự là chính xác, điều đó sẽ không chứng minh rằng cấu trúc đó vẫn ổn.
Peter - Tái lập Monica

6
@ user4581602 "Nó dường như hoạt động khi tôi thử nó" là điều tồi tệ nhất tuyệt đối về hành vi không xác định
HerrJothing

Cần lưu ý rằng câu hỏi là một câu chuyện sai lệch trong cách diễn đạt của nó. Bạn không trả về giá trị của đối số mặc định bằng tham chiếu const, nhưng bạn đang trả về tham chiếu const cho tham chiếu const (... cho đối số mặc định).
Damon

2
@HerrJothing Đồng ý với tuyên bố 100%, nhưng không phải bối cảnh bạn đang sử dụng nó. Cách tôi đọc câu hỏi này giải quyết thành "Khi nào thì hết thời gian của đối tượng?" Tìm ra khi nào hàm hủy được gọi là một cách tốt để làm điều đó. Đối với biến tự động, hàm hủy phải được gọi đúng lúc hoặc bạn gặp vấn đề lớn.
dùng4581602

Câu trả lời:


18

Trong mã của bạn, cả hai s1s3là tài liệu tham khảo lơ lửng. s2s4được

Trong cuộc gọi đầu tiên, std::stringđối tượng trống tạm thời được tạo từ đối số mặc định sẽ được tạo trong ngữ cảnh của biểu thức có chứa cuộc gọi. Do đó, nó sẽ chết ở cuối định nghĩa s1, để lại s1lủng lẳng.

Trong cuộc gọi thứ hai, std::stringđối tượng tạm thời được sử dụng để khởi tạo s2, sau đó nó chết.

Trong cuộc gọi thứ ba, chuỗi ký tự "s"được sử dụng để tạo một std::stringđối tượng tạm thời và cũng chết ở cuối định nghĩa s3, để lại s3lủng lẳng.

Trong cuộc gọi thứ tư, std::stringđối tượng tạm thời có giá trị "s"được sử dụng để khởi tạo s4và sau đó nó chết.

Xem C ++ 17 [class.t tạm thời] /6.1

Một đối tượng tạm thời bị ràng buộc với một tham số tham chiếu trong lệnh gọi hàm (8.2.2) vẫn tồn tại cho đến khi hoàn thành biểu thức đầy đủ có chứa cuộc gọi.


1
Phần thú vị của câu trả lời là sự khẳng định rằng đối số mặc định sẽ được tạo trong ngữ cảnh của trình gọi. Điều đó dường như được hỗ trợ bởi trích dẫn tiêu chuẩn của Guillaume.
Peter - Tái lập Monica

2
@ Peter-ReinstateMonica Xem [expr.call] / 4, "... Việc khởi tạo và hủy từng tham số xảy ra trong bối cảnh của chức năng gọi. ..."
Brian

8

không an toàn :

Nói chung, thời gian tồn tại tạm thời không thể được kéo dài thêm bằng cách "truyền lại": tham chiếu thứ hai, được khởi tạo từ tham chiếu mà tạm thời bị ràng buộc, không ảnh hưởng đến tuổi thọ của nó.


Vì vậy, bạn có nghĩ std::string s2 = foo();là hợp lệ (sau tất cả, không có tài liệu tham khảo nào được thông qua rõ ràng)?
Peter - Tái lập Monica

1
@ Peter-ReinstateMonica rằng một trong những an toàn là đối tượng mới sẽ được xây dựng. Câu trả lời của tôi chỉ đơn thuần là mở rộng suốt đời. Hai câu trả lời khác đã bao gồm tất cả mọi thứ. Tôi sẽ không lặp lại trong tôi.
Oblivion

5

Nó phụ thuộc vào những gì bạn làm với chuỗi sau đó.

Nếu câu hỏi của bạn là mã của tôi chính xác? đúng vậy

Từ [dcl.fct.default] / 2

[ Ví dụ : Tuyên bố

void point(int = 3, int = 4);

khai báo một hàm có thể được gọi bằng 0, một hoặc hai đối số kiểu int. Nó có thể được gọi theo bất kỳ cách nào sau đây:

point(1,2);  point(1);  point();

Hai cuộc gọi cuối cùng tương ứng point(1,4)point(3,4), tương ứng. - ví dụ cuối ]

Vì vậy, mã của bạn có hiệu quả tương đương với:

const std::string& s1 = foo(std::string(""));
std::string s2 = foo(std::string(""));

Tất cả mã của bạn là chính xác, nhưng không có phần mở rộng trọn đời tham chiếu trong bất kỳ trường hợp nào, vì kiểu trả về là tham chiếu.

Vì bạn gọi hàm bằng tạm thời, thời gian tồn tại của chuỗi trả về sẽ không mở rộng câu lệnh.

const std::string& s1 = foo(std::string("")); // okay

s1; // not okay, s1 is dead. s1 is the temporary.

Ví dụ của bạn s2là ổn vì bạn sao chép (hoặc di chuyển) từ tạm thời trước khi kết thúc thời gian bão hòa. s3có cùng một vấn đề hơn s1.

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.