Tại sao toán tử mũi tên trong C ++ chỉ là bí danh của *.?


18

Trong c ++, toán tử * có thể bị quá tải, chẳng hạn như với một trình vòng lặp, nhưng toán tử mũi tên (->) (. *) Không hoạt động với các lớp làm quá tải toán tử *. Tôi tưởng tượng rằng bộ tiền xử lý có thể dễ dàng thay thế tất cả các trường hợp của -> bằng (* trái) .right và điều đó sẽ làm cho các trình vòng lặp đẹp hơn để thực hiện. Có một lý do thực tế cho -> khác biệt, hay đó chỉ là một đặc thù của ngôn ngữ / nhà thiết kế?

Câu trả lời:


16

Quy tắc foo->barbằng (*foo).barchỉ giữ cho các toán tử dựng sẵn.

Unary operator *không luôn luôn có ngữ nghĩa của con trỏ. Tôi có thể tạo một thư viện trong đó nó có nghĩa là hoán vị ma trận, phù hợp với trình phân tích cú pháp không hoặc nhiều hơn, hoặc khá nhiều thứ.

Nó sẽ làm cho ngôn ngữ trở nên khó chịu hơn nếu bất cứ điều gì quá tải unary operator *sẽ đột nhiên đạt được một thứ operator ->bạn không yêu cầu, với ngữ nghĩa có thể không có ý nghĩa.

operator -> là quá tải riêng biệt, vì vậy nếu bạn muốn một cái, bạn có thể quá tải một cái với nỗ lực tối thiểu.

Cũng lưu ý rằng quá tải như vậy sẽ có một số thuộc tính khá thú vị, như tự động thực hiện operator ->các cuộc gọi cho đến khi một trong chuỗi trả về một con trỏ thô. Điều này khá hữu ích cho các con trỏ thông minh và các loại proxy khác.

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <iostream>
#include <ostream>

struct Foo
{
    boost::shared_ptr<std::string> operator -> () const
    {
        return boost::make_shared<std::string>("trololo");
    }
};

int main()
{
    Foo foo;
    std::cerr << foo->size() << std::endl;
}

Ví dụ của bạn minh họa điều gì? Bạn đang trả lại một con trỏ thông minh cho một chuỗi và bằng cách nào đó xuất ra kích thước? Tôi bối rối.
Trevor Hickey

2
Nó minh họa đoạn cuối của câu trả lời của tôi, cách sử dụng ->chuỗi toán tử cho đến khi nó nhận được một con trỏ thô đến một cái gì đó, hội thảo và truy cập một thành viên của nó. Nếu toán tử -> không có chuỗi, ví dụ sẽ không được định dạng vì shared_ptr không phải là con trỏ thô.
Lars Viklund

@LarsViklund: câu trả lời của bạn có vấn đề: bạn đã nói "toán tử-> ... tự động xâu chuỗi toán tử-> cuộc gọi cho đến khi một trong chuỗi trả về một con trỏ thô". Điều này không đúng - sử dụng A->Bchuỗi cú pháp tối đa 1 cuộc gọi bổ sung. Những gì cú pháp nhị phân C ++ -> thực sự không gọi opeartor->trực tiếp cho đối tượng - thay vào đó, nó nhìn vào kiểu Avà kiểm tra xem nó có phải là con trỏ thô không. Nếu sau đó ->nó hủy đăng ký nó và thực thi Btrên đó, nếu không, nó gọi đối tượng operator->, hủy kết quả (sử dụng con trỏ thô gốc hoặc khác operator->và sau đó thực hiện Bkết quả
Guss

@Guss: Tôi không thể tìm thấy bất kỳ chương và câu nào cho khiếu nại của bạn, cũng như không sao chép nó trong trình biên dịch. C ++ 11 13,5,6 / 1 chỉ ra rằng nếu tồn tại quá tải phù hợp, x->msẽ được hiểu là (x.operator->())->m. Nếu LHS là thứ gì đó có quá tải phù hợp operator->trở lại, quá trình này sẽ tái diễn cho đến khi chỉ (*x).mcó hiệu ứng thông thường là 5.2.5 / 2.
Lars Viklund

8

"Ngôn ngữ lập trình C ++" mô tả thực tế là các toán tử này khác nhau để chúng có thể, nhưng cũng nói:

Nếu bạn cung cấp nhiều hơn một trong số các toán tử này, có thể là khôn ngoan khi cung cấp sự tương đương, cũng như khôn ngoan để đảm bảo rằng ++xx+=1có tác dụng tương tự như x=x+1đối với một biến đơn giản xcủa một số lớp nếu ++, + =, =, và + được cung cấp.

Vì vậy, có vẻ như các nhà thiết kế ngôn ngữ đã cung cấp các điểm quá tải riêng biệt bởi vì bạn có thể muốn quá tải chúng khác nhau, thay vì cho rằng bạn luôn muốn chúng giống nhau.


7

Theo nguyên tắc chung, C ++ được thiết kế để ưu tiên tính linh hoạt, do đó quá tải *->tách biệt. Mặc dù điều đó khá bất thường để làm như vậy, nhưng nếu bạn muốn đủ mạnh, bạn có thể viết những sự quá tải đó để làm những việc hoàn toàn khác nhau (ví dụ, có thể có ý nghĩa đối với một ngôn ngữ cụ thể miền được triển khai bên trong C ++).

Điều đó nói rằng, các vòng lặp không hỗ trợ sử dụng. Trên các triển khai cổ xưa, bạn có thể tìm thấy một thư viện yêu cầu (*iter).whateverthay vì iter->whatever, nhưng nếu vậy, đó là một lỗi trong quá trình triển khai, không phải là một đặc điểm của ngôn ngữ. Với số lượng công việc liên quan đến việc thực hiện tất cả các bộ chứa / thuật toán / bộ lặp tiêu chuẩn, không có gì đáng ngạc nhiên khi một số bản phát hành ban đầu có phần chưa hoàn chỉnh, nhưng chúng không bao giờ thực sự có ý định như vậy.


Tôi đã không nhận ra các thùng chứa thư viện tiêu chuẩn được triển khai -> hoặc nó quá tải.
Jakob Weisblat

3
C ++ 03 24.1 / 1 yêu cầu bất kỳ trình vòng lặp nào (*i).mhợp lệ phải hỗ trợ i->mvới cùng một ngữ nghĩa.
Lars Viklund
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.