Đối với trường hợp cụ thể của bạn, quá tải thứ hai là vô ích.
Với mã gốc, chỉ có một lần quá tải cho Load
, chức năng này được gọi cho giá trị và giá trị.
Với mã mới, quá tải đầu tiên được gọi cho giá trị và giá trị thứ hai được gọi cho giá trị. Tuy nhiên, quá tải thứ hai gọi là lần đầu tiên. Cuối cùng, hiệu quả của việc gọi một hoặc cái khác ngụ ý rằng cùng một hoạt động (bất kể việc quá tải đầu tiên nào) sẽ được thực hiện.
Do đó, hiệu ứng của mã gốc và mã mới giống nhau nhưng mã đầu tiên chỉ đơn giản hơn.
Quyết định xem một hàm có phải lấy một đối số theo giá trị, tham chiếu lvalue hoặc tham chiếu rvalue hay không phụ thuộc rất nhiều vào những gì nó thực hiện. Bạn nên cung cấp quá tải tham chiếu rvalue khi bạn muốn di chuyển đối số đã truyền. Có một số tốt references trên di chuyển semantincs ra khỏi đó, vì vậy tôi sẽ không bao gồm nó ở đây.
Bonus:
Để giúp tôi làm cho quan điểm của tôi xem xét probe
lớp này đơn giản:
struct probe {
probe(const char* ) { std::cout << "ctr " << std::endl; }
probe(const probe&) { std::cout << "copy" << std::endl; }
probe(probe&& ) { std::cout << "move" << std::endl; }
};
Bây giờ xem xét chức năng này:
void f(const probe& p) {
probe q(p);
// use q;
}
Calling f("foo");
xuất ra như sau:
ctr
copy
Không có bất ngờ nào ở đây: chúng tôi tạo tạm thời probe
chuyển số const char*
"foo"
. Do đó dòng đầu ra đầu tiên. Sau đó, tạm thời này được liên kết với p
và một bản sao q
của p
được tạo bên trong f
. Do đó dòng đầu ra thứ hai.
Bây giờ, hãy xem xét việc p
theo giá trị, nghĩa là, thay đổi f
tới:
void f(probe p) {
// use p;
}
Kết quả của f("foo");
tại
ctr
là Một số sẽ ngạc nhiên rằng trong trường hợp này: không có sao chép! Nói chung, nếu bạn lấy một đối số bằng cách tham chiếu và sao chép nó bên trong hàm của bạn, thì tốt hơn là lấy đối số theo giá trị. Trong trường hợp này, thay vì tạo tạm thời và sao chép nó, trình biên dịch có thể xây dựng đối số (p
trong trường hợp này) trực tiếp từ đầu vào ("foo"
). Để biết thêm thông tin, xem Want Speed? Pass by Value. bởi Dave Abrahams.
Có hai ngoại lệ đáng chú ý cho hướng dẫn này: nhà thầu và nhà điều hành chuyển nhượng.
xem xét lớp này:
struct foo {
probe p;
foo(const probe& q) : p(q) { }
};
Các nhà xây dựng phải mất một probe
bằng cách tham chiếu const và sau đó sao chép nó vào p
. Trong trường hợp này, theo hướng dẫn ở trên không mang lại bất kỳ cải thiện hiệu suất nào và hàm tạo bản sao của probe
sẽ được gọi là anyway. Tuy nhiên, việc lấy q
theo giá trị có thể tạo ra một vấn đề về độ phân giải quá tải tương tự như vấn đề với toán tử gán mà bây giờ tôi sẽ đề cập đến.
Giả sử rằng lớp học của chúng tôi probe
có phương pháp không ném swap
. Sau đó, việc thực hiện đề nghị của toán tử gán của nó (suy nghĩ trong C++ 03 thuật ngữ trong thời gian tới) được
probe& operator =(const probe& other) {
probe tmp(other);
swap(tmp);
return *this;
}
Sau đó, theo phương châm trên, nó tốt hơn để viết nó như thế này
probe& operator =(probe tmp) {
swap(tmp);
return *this;
}
Bây giờ, hãy nhập C++ 11 với tham chiếu rvalue và di chuyển ngữ nghĩa. Bạn đã quyết định thêm toán tử chuyển nhượng di chuyển:
probe& operator =(probe&&);
Bây giờ hãy gọi toán tử gán tạm thời tạo ra sự mơ hồ vì cả quá tải đều khả thi và không được ưu tiên hơn. Để giải quyết vấn đề này, sử dụng thực hiện ban đầu của toán tử gán (lấy đối số bằng tham chiếu const).
Thực ra, vấn đề này không dành riêng cho các nhà xây dựng và nhà điều hành chuyển nhượng và có thể xảy ra với bất kỳ chức năng nào. (Đó là nhiều khả năng là bạn sẽ trải nghiệm nó với nhà thầu và các toán tử gán mặc dù.) Ví dụ, gọi g("foo");
khi g
đã sau hai quá tải làm tăng sự nhập nhằng:
void g(probe);
void g(probe&&);
'Để giải quyết vấn đề này, hãy sử dụng thực thi gốc của toán tử gán (lấy đối số bằng tham chiếu const) .' Thay vào đó, chỉ cần áp dụng thành phần sao chép và trao đổi chung bằng cách thực hiện một hàm khởi động di chuyển và để trình biên dịch quyết định sao chép - hoặc di chuyển - xây dựng biến 'tmp' cho toán tử gán. – SebNag