2012-06-26 34 views
55

C++ 11 hỗ trợ một chức năng cú pháp mới:Kiểu kiểu cú pháp kiểu trả về có trở thành mặc định cho các chương trình C++ 11 mới không?

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

Hiện tại chức năng này sẽ được khai báo là:

int func_name(int x, int y); 

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

Tuy nhiên, kiểu 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 chỉ được sử dụng khi cần?

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

+23

Nó có hầu hết cho 'decltype' trên đối số. –

+0

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

+0

@ Plus Plus Plus Điều này có nghĩa rằng bạn để lại những thứ như họ đang ở trong C + + 03, trừ khi bạn cần phải lấy được kiểu trả về? – mirk

Trả lời

71

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

Loại trả về theo sau có một số lợi thế nhỏ khác. Ví dụ, hãy xem xét một tổ chức phi inline định nghĩa hàm thành viên bằng cách sử dụng cú pháp chức năng 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 
{ 
    // ... 
} 

typedefs Thành viên đang không ở 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 tôi phải lặp lại trình độ lớp hai lần . Nếu chúng ta sử dụng một kiểu trả về trailing, chúng tôi không cần phải lặp lại tên của các loại:

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ư vậy, 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 định nghĩa nội tuyến, sau đó 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++ Bây giờ năm 2012, Alisdair Meredith chỉ ra rằng nếu bạn sử dụng trailing loại trở lại một cách nhất quán, tên của tất cả các chức năng của bạn xếp hàng gọn gàng:

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

Tôi đã sử dụng các kiểu trả về theo sau ở mọi nơi trong CxxReflect, vì vậy nếu bạn đang tìm kiếm một ví dụ về cách mã sử dụng chúng một cách nhất quán, bạn có thể xem ở đó (ví dụ: the type class).

+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

+0

Xin chào, James. Câu trả lời này có lẽ có thể được thực hiện chính xác hơn theo tiêu chuẩn C++ 14. –

6

Xem bài viết tốt đẹp này: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html dụ Rất tốt khi sử dụng cú pháp này mà không decltype 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 rực rỡ cũng bị đánh cắp từ bài viết Alex Allain của "Bởi vì giá trị lợi nhuận càng ở phần cuối của chức năng, thay vì trước đó, bạn không cần phải thêm phạm vi lớp."

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

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

Tôi không chắc rằng điều này rơi vào danh mục "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ể có hậu quả tai hại; lỗi thời gian biên dịch, không quá nhiều. –

+2

@JamesMcNellis so sánh đầu ra trình biên dịch: 'prog.cpp: 13: 12: lỗi: nguyên mẫu cho 'PersonType Person :: getPersonType()' không khớp với bất kỳ trong lớp 'Person'' so với ' prog.cpp: 13: 1: lỗi: 'PersonType' không đặt tên một loại' Lỗi đầu tiên từ trình biên dịch là, ít nhất là đối với tôi, tệ hơn để hiểu. – PiotrNycz

46

Ngoài những gì người khác nói, sự trở lại dấu gõ cũng cho phép sử dụng this, được không nếu 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 tuyên bố thứ hai, chúng tôi sử dụng phong cách truyền thống. Tuy nhiên kể từ khi this không được phép tại vị trí đó, trình biên dịch d oes không ngầm sử dụng nó. Vì vậy, a.end() sử dụng loại được khai báo tĩnh a để xác định số tiền end quá tải của vector<int> nó sẽ gọi, kết thúc là phiên bản không phải const.

7

Một ưu điểm khác là cú pháp kiểu trả về có thể đọc được nhiều hơn khi hàm trả về một con trỏ tới 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ể được thực hiện đơn giản bằng cách giới thiệu một loại bí danh cho các con trỏ hàm:

using FuncPtr = void (*)(int); 
FuncPtr get_func_on(int i); 
Các vấn đề liên quan