Kiểu cú pháp kiểu trả về theo sau có nên trở thành mặc định cho các chương trình C ++ 11 mới không? [đóng cửa]


92

C ++ 11 hỗ trợ cú pháp hàm mới:

auto func_name(int x, int y) -> int;

Hiện tại hàm này sẽ được khai báo là:

int func_name(int x, int y);

Phong cách mới dường như vẫn chưa được áp dụng rộng rãi (nói trong stl gcc)

Tuy nhiên, phong cách mới này có nên được ưa thích ở mọi nơi trong các chương trình C ++ 11 mới hay nó chỉ được sử dụng khi cần thiết?

Cá nhân tôi thích phong cách cũ hơn khi có thể, nhưng một cơ sở mã với các phong cách hỗn hợp trông khá xấu.


29
Nó ở đó chủ yếu cho các decltypecuộc tranh luận.
Cat Plus Plus

những gì CatPlusPlus nói: làm cho không có nhiều ý nghĩa sử dụng nó trong ví dụ của bạn
Stijn

@Cat Plus Plus Điều này có nghĩa là bạn để mọi thứ như cũ trong C ++ 03, trừ khi bạn cần lấy kiểu trả về?
mirk

1
Xấu xí khi phải chỉ định "tự động" trước mọi hàm. Đó có phải là câu trả lời khó hiểu của C ++ cho "def" của python không?
Erik Aronesty

Câu trả lời:


109

Có một số trường hợp bạn phải sử dụng kiểu trả về theo sau. Đáng chú ý nhất, kiểu trả về lambda, nếu được chỉ định, phải được chỉ định thông qua kiểu trả về theo sau. Ngoài ra, nếu kiểu trả về của bạn sử dụng một decltypeyêu cầu tên đối số phải nằm trong phạm vi, thì kiểu trả về theo sau phải được sử dụng (tuy nhiên, một kiểu thường có thể sử dụng declval<T>để giải quyết vấn đề sau này).

Kiểu trả về theo sau có một số ưu điểm nhỏ khác. Ví dụ: hãy xem xét một định nghĩa hàm thành viên không nội tuyến bằng cách sử dụng cú pháp hàm truyền thống:

struct my_awesome_type
{
    typedef std::vector<int> integer_sequence;

    integer_sequence get_integers() const;
}; 

my_awesome_type::integer_sequence my_awesome_type::get_integers() const
{
    // ...
}

Các typedef thành viên không nằm trong phạm vi cho đến khi tên của lớp xuất hiện trước đó ::get_integers, vì vậy chúng ta phải lặp lại tiêu chuẩn của lớp hai lần. Nếu chúng ta sử dụng kiểu trả về theo sau, chúng ta không cần lặp lại tên của kiểu:

auto my_awesome_type::get_integers() const -> integer_sequence
{
    // ...
}

Trong ví dụ này, nó không phải là một vấn đề lớn, nhưng nếu bạn có tên lớp dài hoặc hàm thành viên của các mẫu lớp không được xác định nội tuyến, thì nó có thể tạo ra sự khác biệt lớn về khả năng đọc.

Trong phiên "Fresh Paint" của mình tại C ++ Now 2012, Alisdair Meredith đã chỉ ra rằng nếu bạn sử dụng các kiểu trả về theo sau một cách nhất quán, thì tên của tất cả các hàm của bạn sẽ được sắp xếp gọn gàng:

auto foo() -> int;
auto bar() -> really_long_typedef_name;

Tôi đã sử dụng trailing kiểu trả về ở khắp mọi nơi trong CxxReflect , vì vậy nếu bạn đang tìm kiếm một ví dụ về mã vẻ sử dụng chúng như thế nào một cách nhất quán, bạn có thể có một cái nhìn ở đó (ví dụ, các typelớp ).


1
Có vẻ như chưa có sự đồng thuận, nhưng thật thú vị khi nhìn vào CxxReflect với phong cách mới.
mirk

Chào James. Câu trả lời này có thể được thực hiện chính xác hơn dựa trên tiêu chuẩn C ++ 14.
Drew Dormann

@DrewDormann Bạn sẽ thêm / thay đổi điều gì?
underscore_d

Căn chỉnh thực sự là một điểm cộng lớn, đến mức tôi ước sẽ có một từ khóa 'func' mới để thay thế từ 'auto' vô nghĩa ở đây.
Johan Boulé

67

Ngoài những gì người khác đã nói, kiểu trả về theo sau cũng cho phép sử dụng this, kiểu khác thì không được phép

struct A {
  std::vector<int> a;

  // OK, works as expected
  auto begin() const -> decltype(a.begin()) { return a.begin(); }

  // FAIL, does not work: "decltype(a.end())" will be "iterator", but 
  // the return statement returns "const_iterator"
  decltype(a.end()) end() const { return a.end(); }
};

Trong khai báo thứ hai, chúng tôi sử dụng kiểu truyền thống. Tuy nhiên vì thiskhông được phép ở vị trí đó, trình biên dịch không mặc nhiên sử dụng nó. Vì vậy, a.end()sử dụng kiểu được khai báo tĩnh ađể xác định endquá tải của vector<int>nó sẽ gọi là gì, kết thúc là phiên bản không phải const.


2
Mặc dù đây là một minh chứng tốt / gọn gàng về khái niệm (sử dụng các thành viên trong các kiểu trả về), thật buồn cười vì trong C ++ 14, việc chỉ định một kiểu là hoàn toàn thừa trong một định nghĩa nội tuyến không có chuyển đổi; bây giờ chúng ta chỉ có thể sử dụng khấu trừ loại trả lại đầy đủ. : P
underscore_d.

27

Một ưu điểm khác là cú pháp kiểu dấu-trả lại có thể dễ đọc hơn khi hàm trả về một con trỏ cho một hàm. Ví dụ, so sánh

void (*get_func_on(int i))(int);

với

auto get_func_on(int i) -> void (*)(int);

Tuy nhiên, người ta có thể tranh luận rằng khả năng đọc tốt hơn có thể đạt được chỉ đơn giản bằng cách giới thiệu bí danh kiểu cho con trỏ hàm:

using FuncPtr = void (*)(int);
FuncPtr get_func_on(int i);

10

Hãy xem bài viết hay này: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value- After- Chức năng.html Ví dụ rất hay khi sử dụng cú pháp này mà không cần khai báo trong trò chơi :

class Person
{
public:
    enum PersonType { ADULT, CHILD, SENIOR };
    void setPersonType (PersonType person_type);
    PersonType getPersonType ();
private:
    PersonType _person_type;
};

auto Person::getPersonType () -> PersonType
{
    return _person_type;
}

Và lời giải thích tuyệt vời cũng bị đánh cắp từ bài báo của Alex Allain "Bởi vì giá trị trả về nằm ở cuối hàm, thay vì trước nó, bạn không cần thêm phạm vi lớp."

So sánh với trường hợp có thể xảy ra này khi một người tình cờ quên phạm vi lớp và, đối với thảm họa lớn hơn, một PersonType khác được xác định trong phạm vi toàn cầu:

typedef float PersonType; // just for even more trouble
/*missing: Person::*/
PersonType Person::getPersonType ()
{
    return _person_type;
}

7
Tôi không chắc rằng điều này thuộc loại "thảm họa": nếu loại sai, mã sẽ không biên dịch. Lỗi thời gian chạy có thể gây ra hậu quả tai hại; lỗi thời gian biên dịch, không quá nhiều.
James McNellis

4
@JamesMcNellis so sánh đầu ra của trình biên dịch: prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'so với prog.cpp:13:1: error: 'PersonType' does not name a type lỗi đầu tiên từ trình biên dịch, ít nhất là đối với tôi, tệ hơn để hiểu.
PiotrNycz

Cá nhân tôi không đồng ý, tôi thấy thông báo thứ hai khó đọc hơn, và tôi muốn có cách triển khai giống như tuyên bố.
jrh
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.