không thể đạt được kết quả tương tự mà không khai báo rõ ràng một biến auto
?
Tôi sẽ diễn đạt lại câu hỏi của bạn một chút theo cách sẽ giúp bạn hiểu tại sao bạn cần auto
:
Không thể đạt được kết quả tương tự mà không sử dụng trình giữ chỗ kiểu rõ ràng ?
Nó không thể thực hiện được ? Tất nhiên là "có thể". Câu hỏi là liệu nó có xứng đáng với nỗ lực để làm điều đó hay không.
Hầu hết các cú pháp trong các ngôn ngữ khác không có tên kiểu đều hoạt động theo một trong hai cách. Có một cách giống như Go, nơi name := value;
khai báo một biến. Và có một cách giống như Python, nơi name = value;
khai báo một biến mới nếu name
trước đó chưa được khai báo.
Giả sử rằng không có vấn đề cú pháp nào khi áp dụng một trong hai cú pháp cho C ++ (mặc dù tôi có thể thấy rằng identifier
theo sau :
trong C ++ có nghĩa là "tạo nhãn"). Vì vậy, bạn mất gì so với trình giữ chỗ?
Chà, tôi không thể làm điều này nữa:
auto &name = get<0>(some_tuple);
Hãy xem, auto
luôn luôn có nghĩa là "giá trị". Nếu bạn muốn lấy một tham chiếu, bạn cần phải sử dụng một cách rõ ràng &
. Và nó sẽ không biên dịch được nếu biểu thức gán là một prvalue. Cả cú pháp dựa trên phép gán đều không có cách nào để phân biệt giữa các tham chiếu và giá trị.
Bây giờ, bạn có thể thực hiện các cú pháp gán như vậy để suy ra các tham chiếu nếu giá trị đã cho là một tham chiếu. Nhưng điều đó có nghĩa là bạn không thể làm:
auto name = get<0>(some_tuple);
Điều này sao chép từ bộ, tạo ra một đối tượng độc lập với some_tuple
. Đôi khi, đó chính xác là những gì bạn muốn. Điều này thậm chí còn hữu ích hơn nếu bạn muốn chuyển từ tuple với auto name = get<0>(std::move(some_tuple));
.
OK, vì vậy có lẽ chúng ta có thể mở rộng các cú pháp này một chút để giải thích sự khác biệt này. Có thể &name := value;
hoặc &name = value;
có nghĩa là để suy ra một tài liệu tham khảo như thế nào auto&
.
Được rồi. Cái này thì sao:
decltype(auto) name = some_thing();
Ờ, đúng vậy; C ++ thực sự có hai trình giữ chỗ: auto
vàdecltype(auto)
. Ý tưởng cơ bản của việc khấu trừ này là nó hoạt động chính xác như thể bạn đã làm decltype(expr) name = expr;
. Vì vậy, trong trường hợp của chúng ta, nếu some_thing()
là một đối tượng, nó sẽ suy ra một đối tượng. Nếu some_thing()
là một tham chiếu, nó sẽ suy ra một tham chiếu.
Điều này rất hữu ích khi bạn đang làm việc trong mã mẫu và không chắc chắn chính xác giá trị trả về của một hàm sẽ là gì. Điều này rất tốt cho việc chuyển tiếp và nó là một công cụ thiết yếu, ngay cả khi nó không được sử dụng rộng rãi.
Vì vậy, bây giờ chúng ta cần thêm nhiều hơn vào cú pháp của mình. name ::= value;
có nghĩa là "làm những gì decltype(auto)
hiện". Tôi không có phiên bản tương đương cho biến thể Pythonic.
Nhìn vào cú pháp này, không phải là bạn dễ vô tình nhập sai? Không chỉ vậy, nó hầu như không tự ghi lại. Ngay cả khi bạn chưa từng thấy decltype(auto)
trước đây, nó đủ lớn và rõ ràng để bạn ít nhất có thể dễ dàng nhận ra rằng có điều gì đó đặc biệt đang diễn ra. Trong khi sự khác biệt trực quan giữa ::=
và :=
là tối thiểu.
Nhưng đó là ý kiến; có nhiều vấn đề thực chất hơn. Hãy xem, tất cả những điều này đều dựa trên việc sử dụng cú pháp gán. Chà ... còn những chỗ bạn không thể sử dụng cú pháp gán thì sao? Như thế này:
for(auto &x : container)
Chúng ta có thay đổi điều đó thành for(&x := container)
không? Bởi vì điều đó dường như đang nói điều gì đó rất khác so với dựa trên phạm vi for
. Có vẻ như đó là câu lệnh khởi tạo từ một for
vòng lặp thông thường , không phải dựa trên phạm vi for
. Nó cũng sẽ là một cú pháp khác với các trường hợp không được suy luận.
Ngoài ra, việc sao chép-khởi tạo (sử dụng =
) không giống trong C ++ với khởi tạo trực tiếp (sử dụng cú pháp hàm tạo). Vì vậy, name := value;
có thể không hoạt động trong trường hợp auto name(value)
sẽ có.
Chắc chắn, bạn có thể khai báo rằng :=
sẽ sử dụng khởi tạo trực tiếp, nhưng điều đó sẽ khá tương đồng với cách phần còn lại của C ++ hoạt động.
Ngoài ra, còn một thứ nữa: C ++ 14. Nó cung cấp cho chúng tôi một tính năng khấu trừ hữu ích: khấu trừ loại trả lại. Nhưng điều này dựa trên trình giữ chỗ. Cũng giống như dựa trên phạm vi for
, về cơ bản nó dựa trên một tên kiểu được trình biên dịch điền vào, không phải bởi một số cú pháp áp dụng cho một tên và biểu thức cụ thể.
Hãy xem, tất cả những vấn đề này đều xuất phát từ cùng một nguồn: bạn đang phát minh ra cú pháp hoàn toàn mới để khai báo các biến. Khai báo dựa trên trình giữ chỗ không cần phải phát minh ra cú pháp mới . Họ đang sử dụng cùng một cú pháp như trước đây; họ chỉ đang sử dụng một từ khóa mới hoạt động giống như một loại, nhưng có một ý nghĩa đặc biệt. Đây là những gì cho phép nó hoạt động dựa trên phạm vi for
và loại trừ kiểu trả về. Nó là thứ cho phép nó có nhiều dạng ( auto
so với decltype(auto)
). Và kể từ đó trở đi.
Trình giữ chỗ hoạt động vì chúng là giải pháp đơn giản nhất cho vấn đề, đồng thời giữ lại tất cả các lợi ích và tính tổng quát của việc sử dụng tên kiểu thực tế. Nếu bạn nghĩ ra một giải pháp thay thế khác hoạt động phổ biến như trình giữ chỗ, thì rất khó có khả năng nó sẽ đơn giản như trình giữ chỗ.
Trừ khi nó chỉ là đánh vần các trình giữ chỗ với các từ khóa hoặc ký hiệu khác nhau ...