Một số sử dụng của dectype (tự động) là gì?


151

Trong c ++ 14 decltype(auto)thành ngữ được giới thiệu.

Thông thường việc sử dụng nó là cho phép autokhai báo sử dụng các decltypequy tắc trên biểu thức đã cho .

Tìm kiếm các ví dụ về cách sử dụng "tốt" của thành ngữ Tôi chỉ có thể nghĩ về những thứ như sau (của Scott Meyers ), cụ thể là khấu trừ kiểu trả về của hàm :

template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

Có ví dụ nào khác mà tính năng ngôn ngữ mới này hữu ích không?


2
bài viết này về cơ bản đề nghị cố gắng tránh thành ngữ này bởi vì khi sử dụng nó, bạn sẽ cung cấp ít tùy chọn hơn để tối ưu hóa cho trình biên dịch stackoverflow.com/a/20092875/2485710
user2485710

Tôi đã từng sử dụng decltype(auto)cho một cái gì đó tương tự template<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; }, mặc dù sau đó tôi nhận ra rằng tôi phải sử dụng return (p.first);cái nào đáng ngạc nhiên (nhưng IIRC điều này thậm chí còn có ý định).
dyp

@ user2485710 không chắc chắn về việc tối ưu hóa cụ thể, nhiều khả năng xảy ra tai nạn nếu decltype(auto)có thể khiến một cái gì đó được sao chép / di chuyển vào đối tượng được khai báo, trái với dự đoán.
gạch dưới

Câu trả lời:


170

Chuyển tiếp kiểu trả về trong mã chung

Đối với mã không chung chung, như ví dụ ban đầu bạn đã đưa ra, bạn có thể chọn thủ công để lấy tham chiếu làm loại trả về:

auto const& Example(int const& i) 
{ 
    return i; 
}

nhưng trong mã chung, bạn muốn có thể chuyển tiếp hoàn hảo một kiểu trả về mà không cần biết bạn đang xử lý một tham chiếu hay một giá trị. decltype(auto)cung cấp cho bạn khả năng đó:

template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}

Trì hoãn khấu trừ kiểu trả về trong các mẫu đệ quy

Trong Hỏi & Đáp này vài ngày trước, một đệ quy vô hạn trong quá trình khởi tạo mẫu đã gặp phải khi kiểu trả về của mẫu được chỉ định decltype(iter(Int<i-1>{}))thay vì decltype(auto).

template<int i> 
struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) 
{ return iter(Int<i-1>{}); }

int main() { decltype(iter(Int<10>{})) a; }

decltype(auto)được sử dụng ở đây để trì hoãn việc khấu trừ kiểu trả về sau khi bụi của khởi tạo mẫu đã lắng xuống.

Công dụng khác

Bạn cũng có thể sử dụng decltype(auto)trong các bối cảnh khác, ví dụ dự thảo Tiêu chuẩn N3936 cũng nêu rõ

7.1.6.4 thông số kỹ thuật tự động [dcl.spec.auto]

1 Kiểu autodecltype(auto)thông số kỹ thuật chỉ định loại giữ chỗ sẽ được thay thế sau này, bằng cách khấu trừ từ trình khởi tạo hoặc bằng cách xác định rõ ràng với kiểu trả về theo sau. Các autoloại-Speci fi er cũng được sử dụng để biểu thị rằng một lambda là một lambda generic.

2 Loại giữ chỗ có thể xuất hiện cùng với một công cụ khai báo hàm trong dec-specifier-seq, type-specifier-seq, convert-function-id hoặc trailing-return-type, trong bất kỳ ngữ cảnh nào mà trình khai báo đó hợp lệ . Nếu bộ khai báo hàm bao gồm kiểu trả về theo sau (8.3.5), chỉ định kiểu trả về được khai báo của hàm. Nếu loại trả về được khai báo của hàm chứa loại giữ chỗ, loại trả về của hàm được suy ra từ các câu lệnh trả về trong phần thân của hàm, nếu có.

Dự thảo cũng chứa ví dụ về khởi tạo biến này:

int i;
int&& f();
auto x3a = i;                  // decltype(x3a) is int
decltype(auto) x3d = i;        // decltype(x3d) is int
auto x4a = (i);                // decltype(x4a) is int
decltype(auto) x4d = (i);      // decltype(x4d) is int&
auto x5a = f();                // decltype(x5a) is int
decltype(auto) x5d = f();      // decltype(x5d) is int&&
auto x6a = { 1, 2 };           // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i;                // decltype(x7a) is int*
decltype(auto)*x7d = &i;       // error, declared type is not plain decltype(auto)

17
Là hành vi khác nhau của (i)vs imột điều mới trong C ++ 14?
Danvil

14
@Danvil decltype(expr)và đã decltype((expr))khác trong C ++ 11 rồi, điều này khái quát hành vi đó.
TemplateRex

13
Tôi mới học được điều này, cảm thấy như một quyết định thiết kế tồi tệ ... thêm một sắc thái đúng giờ vào nghĩa cú pháp của dấu ngoặc đơn.
Kahler

Ví dụ luôn nhắc nhở sự ghê tởm này là cú pháp tập tin một chuỗi (cũng được đề cập trong liên kết đó). Mỗi phần của nó dường như lạc hậu. Bạn có thể không mong đợi sự mơ hồ nào cả, và loại bỏ dấu ngoặc thừa khỏi mẫu một cách bắt buộc; bạn sẽ mong đợi sự mơ hồ được giải quyết bằng quá trình loại bỏ theo SFINAE, nhưng sẽ là ứng cử viên khác ngoài tuyên bố được loại bỏ trước (SF AE); và trong thất vọng, bạn có thể tiếp tục ngay khi nó biên dịch suy nghĩ các parens tùy ý giải quyết sự mơ hồ, nhưng họ giới thiệu nó. Hầu hết các vexing cho các giáo sư CS101 tôi tưởng tượng.
John P

@TemplateRex: Về việc trì hoãn độ phân giải loại trả về trong câu hỏi được tham chiếu: Theo như tôi thấy, trong kịch bản cụ thể , một đơn giản autocũng sẽ thực hiện công việc, vì dù sao kết quả cũng được trả về theo giá trị ... Hoặc tôi đã bỏ lỡ cái gì
Aconcagua

36

Trích dẫn thứ từ đây :

  • decltype(auto)chủ yếu hữu ích cho việc suy ra kiểu trả về của các hàm chuyển tiếp và các hàm bao tương tự , trong đó bạn muốn loại này chính xác theo dõi một số biểu thức mà bạn đang gọi.

  • Ví dụ, đưa ra các chức năng dưới đây:


   string  lookup1();
   string& lookup2();

  • Trong C ++ 11, chúng ta có thể viết các hàm bao bọc sau đây để nhớ duy trì tính tham chiếu của kiểu trả về:

   string  look_up_a_string_1() { return lookup1(); }
   string& look_up_a_string_2() { return lookup2(); }

  • Trong C ++ 14, chúng ta có thể tự động hóa rằng:

   decltype(auto) look_up_a_string_1() { return lookup1(); }
   decltype(auto) look_up_a_string_2() { return lookup2(); }

  • Tuy nhiên, decltype(auto)không nhằm mục đích trở thành một tính năng được sử dụng rộng rãi ngoài điều đó.

  • Cụ thể, mặc dù nó có thể được sử dụng để khai báo các biến cục bộ , nhưng việc đó có lẽ chỉ là một phản mẫu vì tính tham chiếu của biến cục bộ không nên phụ thuộc vào biểu thức khởi tạo.

  • Ngoài ra, nó rất nhạy cảm với cách bạn viết câu lệnh return.

  • Ví dụ, hai hàm dưới đây có các kiểu trả về khác nhau:


   decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
   decltype(auto) look_up_a_string_2() { auto str = lookup2(); return(str); }

  • Trả về đầu tiên string, trả về thứ hai string&, là tham chiếu đến biến cục bộ str.

Từ đề xuất bạn có thể thấy nhiều mục đích sử dụng.


3
Tại sao không chỉ sử dụng autođể trả lại?
Bовић

@ BЈовић cũng có thể hoạt động với khấu trừ loại trả lại tổng quát (nghĩa là autotrả lại) nhưng OP yêu cầu cụ thể cho việc sử dụng decltype(auto).
101010

3
Câu hỏi vẫn còn có liên quan mặc dù. Loại trả về sẽ là auto lookup_a_string() { ... } gì? Có phải nó luôn luôn là một loại không tham khảo? Và do đó auto lookup_a_string() ->decltype(auto) { ... }là cần thiết để buộc phải cho phép các tài liệu tham khảo được (trong một số trường hợp) được trả lại?
Aaron McDaid

@AaronMcDaid Deductible autođược định nghĩa theo thuật ngữ truyền theo mẫu giá trị, vì vậy, nó không thể là một tham chiếu. Xin vui lòng - chờ đợi autocó thể là bất cứ điều gì bao gồm một tài liệu tham khảo, tất nhiên.
tò mò

4
Một ví dụ bổ sung đáng nói là trả về một phần tử của a std::vector. Nói rằng bạn có template<typename T> struct S { auto & operator[](std::size_t i) { return v[i]; } std::vector<T> v; }. Sau đó S<bool>::operator[]sẽ trả lại một tài liệu tham khảo lơ lửng vì chuyên môn hóa std::vector<bool>. Thay đổi kiểu trả về để decltype(auto)giải quyết vấn đề này.
Xoph
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.