Có một số cách để viết swap
, một số tốt hơn so với những cách khác. Tuy nhiên, theo thời gian, nó đã được tìm thấy một định nghĩa duy nhất hoạt động tốt nhất. Chúng ta hãy xem xét cách chúng ta có thể nghĩ về việc viết một swap
chức năng.
Trước tiên chúng ta thấy rằng các thùng chứa như std::vector<>
có một hàm thành viên một đối số swap
, chẳng hạn như:
struct vector
{
void swap(vector&) { /* swap members */ }
};
Đương nhiên, sau đó, lớp học của chúng ta cũng nên, phải không? Vâng, không thực sự. Thư viện tiêu chuẩn có tất cả những thứ không cần thiết , và một thành viên swap
là một trong số đó. Tại sao? Hãy tiếp tục đi.
Những gì chúng ta nên làm là xác định những gì hợp quy, và những gì lớp chúng ta cần làm để làm việc với nó. Và phương pháp hoán đổi kinh điển là với std::swap
. Đây là lý do tại sao các chức năng thành viên không hữu ích: chúng không phải là cách chúng ta trao đổi mọi thứ, nói chung và không có liên quan đến hành vi của std::swap
.
Vậy thì, để thực hiện std::swap
công việc chúng ta nên cung cấp (và std::vector<>
nên cung cấp) một chuyên môn std::swap
, phải không?
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
Chà điều đó chắc chắn sẽ hoạt động trong trường hợp này, nhưng nó có một vấn đề rõ ràng: chuyên môn hóa chức năng không thể là một phần. Đó là, chúng tôi không thể chuyên môn hóa các lớp mẫu với điều này, chỉ có các cảnh báo cụ thể:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
Phương pháp này hoạt động một số thời gian, nhưng không phải tất cả thời gian. Phải có cách tốt hơn.
Có! Chúng ta có thể sử dụng một friend
hàm và tìm thấy nó thông qua ADL :
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
Khi chúng tôi muốn trao đổi một cái gì đó, chúng tôi kết hợp † std::swap
và sau đó thực hiện cuộc gọi không đủ điều kiện:
using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first
// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap
Một friend
chức năng là gì? Có sự nhầm lẫn xung quanh khu vực này.
Trước khi C ++ được chuẩn hóa, các friend
hàm đã thực hiện một cái gì đó gọi là "tiêm tên bạn bè", trong đó mã hoạt động như thể hàm được viết trong không gian tên xung quanh. Ví dụ: đây là những tiêu chuẩn tương đương:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
Tuy nhiên, khi ADL được phát minh, điều này đã bị xóa. Các friend
chức năng có thể sau đó chỉ được tìm thấy qua ADL; nếu bạn muốn nó là một hàm miễn phí, thì nó cần phải được khai báo như vậy ( ví dụ, xem cái này ). Nhưng lo! Có một vấn đề.
Nếu bạn chỉ sử dụng std::swap(x, y)
, tình trạng quá tải của bạn sẽ không bao giờ được tìm thấy, bởi vì bạn đã nói rõ ràng "nhìn vào std
, và không nơi nào khác"! Đây là lý do tại sao một số người đề nghị viết hai hàm: một là hàm được tìm thấy thông qua ADL và hàm kia để xử lý các std::
bằng cấp rõ ràng .
Nhưng như chúng ta đã thấy, điều này không thể hoạt động trong mọi trường hợp và chúng ta kết thúc với một mớ hỗn độn xấu xí. Thay vào đó, hoán đổi thành ngữ đã đi theo con đường khác: thay vì biến nó thành công việc của các lớp để cung cấp std::swap
, đó là công việc của người trao đổi để đảm bảo họ không sử dụng đủ điều kiện swap
, như trên. Và điều này có xu hướng hoạt động khá tốt, miễn là mọi người biết về nó. Nhưng vấn đề nằm ở chỗ: thật không trực quan khi cần sử dụng một cuộc gọi không đủ tiêu chuẩn!
Để làm cho điều này dễ dàng hơn, một số thư viện như Boost đã cung cấp chức năng boost::swap
, chỉ thực hiện một cuộc gọi không đủ tiêu chuẩn swap
, với std::swap
không gian tên liên quan. Điều này giúp làm cho mọi thứ trở nên cô đọng một lần nữa, nhưng nó vẫn là một người lập dị.
Lưu ý rằng không có thay đổi trong C ++ 11 đối với hành vi std::swap
, điều mà tôi và những người khác nghĩ nhầm sẽ là trường hợp. Nếu bạn đã từng chút bởi điều này, đọc ở đây .
Tóm lại: chức năng thành viên chỉ là tiếng ồn, chuyên môn hóa là xấu xí và không đầy đủ, nhưng friend
chức năng đã hoàn thành và hoạt động. Và khi bạn trao đổi, hoặc sử dụng boost::swap
hoặc không đủ tiêu chuẩn swap
với std::swap
liên quan.
Một cách không chính thức, một tên được liên kết nếu nó sẽ được xem xét trong khi gọi hàm. Để biết chi tiết, đọc §3.4.2. Trong trường hợp này, std::swap
thông thường không được xem xét; nhưng chúng ta có thể liên kết nó (thêm nó vào tập hợp các tình trạng quá tải được xem xét bởi không đủ tiêu chuẩn swap
), cho phép tìm thấy nó.