Không có gì trở lại của câu lệnh {} trong câu lệnh C ++ 11?


115

Tuyên bố gì

return {};

trong C ++ 11 chỉ ra và khi nào nên sử dụng nó thay vì (nói)

return NULL;

hoặc là

return nullptr;

59
nó trả về một thể hiện được xây dựng mặc định của kiểu trả về của hàm.
Richard Hodges

Hay nó đơn giản return;không có giá trị?
i486

Không, như cuộc thảo luận tiết lộ, đó là lỗi thời gian biên dịch nếu hàm của bạn sẽ trả về một cái gì đó (tức là không phải kiểu trả về void) và bạn chỉ viết return; Mặt khác return{};là hợp lệ nếu bạn có loại trả về.
Pedia

@Pedia Không phải lúc nào, một số đối tượng sẽ yêu cầu các đối số để xây dựng
MM

Câu trả lời:


108

return {};cho biết "trả về một đối tượng của kiểu trả về của hàm được khởi tạo với một trình khởi tạo danh sách trống ". Hành vi chính xác phụ thuộc vào loại đối tượng được trả về.

Từ cppreference.com (vì OP được gắn thẻ C ++ 11, tôi đã loại trừ các quy tắc trong C ++ 14 và C ++ 17; tham khảo liên kết để biết thêm chi tiết):

  • Nếu danh sách braces-init-trống và T là một loại lớp với hàm tạo mặc định, việc khởi tạo giá trị được thực hiện.
  • Mặt khác, nếu T là loại tổng hợp, việc khởi tạo tổng hợp được thực hiện.
  • Mặt khác, nếu T là một chuyên môn của std :: initizer_list, thì đối tượng T được khởi tạo trực tiếp hoặc khởi tạo sao chép, tùy thuộc vào ngữ cảnh, từ danh sách braces-init-list.
  • Mặt khác, các hàm tạo của T được xem xét, theo hai giai đoạn:

    • Tất cả các hàm tạo lấy std :: initizer_list làm đối số duy nhất hoặc làm đối số đầu tiên nếu các đối số còn lại có giá trị mặc định, được kiểm tra và khớp với độ phân giải quá tải đối với một đối số kiểu std :: initizer_list
    • Nếu giai đoạn trước không tạo ra sự trùng khớp, tất cả các nhà xây dựng của T tham gia giải quyết quá tải đối với tập hợp các đối số bao gồm các thành phần của danh sách khởi tạo, với hạn chế chỉ cho phép chuyển đổi không thu hẹp. Nếu giai đoạn này tạo ra một hàm tạo rõ ràng là kết hợp tốt nhất cho khởi tạo danh sách sao chép, thì quá trình biên dịch không thành công (lưu ý, trong khởi tạo sao chép đơn giản, các hàm tạo rõ ràng hoàn toàn không được xem xét).
  • Mặt khác (nếu T không phải là loại lớp), nếu danh sách braces-init-list chỉ có một phần tử và T không phải là loại tham chiếu hoặc là loại tham chiếu tương thích với loại phần tử, T là trực tiếp- khởi tạo (trong khởi tạo danh sách trực tiếp) hoặc khởi tạo sao chép (trong khởi tạo danh sách sao chép), ngoại trừ việc thu hẹp chuyển đổi không được phép.

  • Mặt khác, nếu T là loại tham chiếu không tương thích với loại phần tử. (điều này không thành công nếu tham chiếu là tham chiếu lvalue không const)
  • Mặt khác, nếu danh sách braces-init-list không có phần tử, T được khởi tạo giá trị.

Trước C ++ 11, đối với hàm trả về a std::string, bạn sẽ viết:

std::string get_string() {
    return std::string();
}

Sử dụng cú pháp cú đúp trong C ++ 11, bạn không cần lặp lại kiểu:

std::string get_string() {
    return {}; // an empty string is returned
}

return NULLreturn nullptrnên được sử dụng khi hàm trả về một loại con trỏ:

any_type* get_pointer() {
    return nullptr;
}

Tuy nhiên, NULLkhông được dùng nữa vì C ++ 11 vì nó chỉ là bí danh cho một giá trị nguyên (0), trong khi đó nullptrlà một loại con trỏ thực:

int get_int() {
    return NULL; // will compile, NULL is an integer
}

int get_int() {
    return nullptr; // error: nullptr is not an integer
}

91

Điều này có lẽ khó hiểu:

int foo()
{
  return {};   // honestly, just return 0 - it's clearer
}

Đây có lẽ là không:

SomeObjectWithADefaultConstructor foo()
{
  return {};
  // equivalent to return SomeObjectWithADefaultConstructor {};
}

9
Vì vậy, đó là lỗi thời gian biên dịch nếu kiểu trả về không có hàm tạo mặc định, đúng không?
Pedia

10
Đó là lỗi biên dịch nếu kiểu trả về là một lớp không có hàm tạo mặc định không rõ ràng và không phải là tổng hợp.
Oktalist

3
Nếu loại có hàm initializer_listtạo, sẽ không được sử dụng nếu không có hàm tạo mặc định nào khả dụng?
celtschk

4
"Có lẽ khó hiểu"? Đây có phải là lý do tại sao một số linh hồn không tên được gọi là "Sự tục tĩu cồng kềnh đó là C ++"? Có thể bất cứ điều gì tiết kiệm trong tổ hợp phím này có thể cung cấp khả năng cho sự thiếu rõ ràng mà nó cung cấp không? Đây là một câu hỏi chân thành. Hãy thuyết phục tôi bằng những ví dụ thực tế.
MickeyfAgain_B BeforeExitOfSO

4
return {}KHÔNG tương đương vớireturn SomeObjectWithADefaultConstructor{};
MM

26

return {};có nghĩa {}là bộ khởi tạo cho giá trị trả về . Giá trị trả về được khởi tạo danh sách với một danh sách trống.


Dưới đây là một số thông tin cơ bản về giá trị trả về , dựa trên [stmt.return] trong Tiêu chuẩn C ++:

Đối với một hàm trả về theo giá trị (nghĩa là kiểu trả về không phải là tham chiếu và không void), có một đối tượng tạm thời được gọi là giá trị trả về . Đối tượng này được tạo bởi returncâu lệnh và các khởi tạo của nó phụ thuộc vào những gì trong câu lệnh return.

Giá trị trả về tồn tại cho đến khi kết thúc biểu thức đầy đủ trong mã được gọi là hàm; nếu nó có kiểu lớp, thì hàm hủy của nó sẽ chạy trừ khi nó được kéo dài suốt đời bởi người gọi ràng buộc một tham chiếu trực tiếp đến nó.

Giá trị trả về có thể được khởi tạo theo hai cách khác nhau:

  • return some_expression;- giá trị trả về được sao chép khởi tạo từsome_expression
  • return { possibly_empty_list };- giá trị trả về được khởi tạo danh sách từ danh sách.

Giả sử Tlà kiểu trả về của hàm, sau đó lưu ý return T{};khác với return {}: trước đây, tạm thời T{}được tạo và sau đó giá trị trả về được sao chép khởi tạo từ tạm thời đó.

Điều này sẽ không biên dịch được nếu Tkhông có bản sao / move-constructor có thể truy cập được, nhưng return {};sẽ thành công ngay cả khi những hàm tạo đó không có mặt. Theo đó, return T{};có thể hiển thị các tác dụng phụ của trình tạo bản sao, v.v., mặc dù đây là bối cảnh bầu chọn sao chép nên có thể không.


Đây là một bản tóm tắt ngắn gọn về khởi tạo danh sách trong C ++ 14 (N4140 [dcl.init.list] / 3), trong đó trình khởi tạo là một danh sách trống:

  • Nếu Tlà một tổng hợp, thì mỗi thành viên được khởi tạo từ bộ khởi tạo giằng hoặc bằng của nó nếu có, nếu không thì {} áp dụng các bước này theo cách đệ quy).
  • Nếu Tlà một loại lớp với hàm tạo mặc định do người dùng cung cấp, hàm tạo đó được gọi.
  • Nếu Tlà một loại lớp với một hàm tạo mặc định được định nghĩa ngầm định hoặc = defaulted, thì đối tượng được khởi tạo bằng 0 và sau đó hàm tạo mặc định được gọi.
  • Nếu Tlà một std::initializer_list, giá trị trả về là một danh sách trống như vậy.
  • Mặt khác (tức Tlà loại không phải lớp - kiểu trả về không thể là mảng), giá trị trả về là không khởi tạo.

Tổng hợp init xuất hiện trước và nó khởi tạo đệ quy mọi thành viên với {}, có thể có hoặc không có giá trị init.
TC

@TC đúng, tôi đã đi bằng cppreference nhưng đã bỏ qua một "cho đến khi C ++ 14"
MM

3

Đây là một loại tay ngắn cho một thể hiện mới của kiểu trả về của phương thức.

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.