làm thế nào để cung cấp một chức năng hoán đổi cho lớp của tôi?


87

Cách thích hợp để kích hoạt các swapthuật toán STL của tôi là gì?

1) Thành viên swap. Có std::swapsử dụng thủ thuật SFINAE để sử dụng thành viên swap.

2) Đứng tự do swaptrong cùng một không gian tên.

3) Chuyên môn hóa từng phần của std::swap.

4) Tất cả những điều trên.

Cảm ơn bạn.

CHỈNH SỬA: Có vẻ như tôi đã không nói rõ câu hỏi của mình. Về cơ bản, tôi có một lớp mẫu và tôi cần STL algos để sử dụng phương thức hoán đổi (hiệu quả) mà tôi đã viết cho lớp đó.

Câu trả lời:


94
  1. là thích hợp sử dụng của swap. Viết nó theo cách này khi bạn viết mã "thư viện" và muốn bật ADL (tra cứu phụ thuộc vào đối số) swap. Ngoài ra, điều này không liên quan gì đến SFINAE.
// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs) {
    using std::swap; // enable 'std::swap' to be found
                    // if no other 'swap' is found through ADL
    // some code ...
    swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                    // or falls back on 'std::swap'
    // more code ...
}
  1. Là cách thích hợp để cung cấp một swapchức năng cho lớp của bạn.
namespace Foo {

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs) {
    // ...
}

}

Nếu swapbây giờ được sử dụng như trong 1), hàm của bạn sẽ được tìm thấy. Ngoài ra, bạn có thể đặt chức năng đó làm bạn bè nếu bạn thực sự cần, hoặc cung cấp một thành viên swapđược gọi bằng chức năng miễn phí:

// version 1
class Bar{
public:
    friend void swap(Bar& lhs, Bar& rhs) {
    // ....
    }
};

// version 2
class Bar{
public:
    void swap(Bar& other) {
    // ...
    }
};

void swap(Bar& lhs, Bar& rhs) {
    lhs.swap(rhs);
}

...
  1. Ý bạn là một chuyên môn rõ ràng. Một phần vẫn là một cái gì đó khác và cũng không thể cho các hàm, chỉ có cấu trúc / lớp. Như vậy, vì bạn không thể chuyên biệt std::swapcho các lớp mẫu, bạn phải cung cấp một hàm miễn phí trong không gian tên của mình. Không phải là một điều xấu, nếu tôi có thể nói như vậy. Bây giờ, một chuyên môn hóa rõ ràng cũng có thể thực hiện được, nhưng nói chung bạn không muốn chuyên môn hóa một mẫu hàm :
namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
    // ...
}

}
  1. Không, vì 1) khác với 2) và 3). Ngoài ra, có cả 2) và 3) sẽ dẫn đến việc luôn luôn có 2) được chọn, vì nó phù hợp hơn.

8
Câu hỏi (1) của bạn và câu hỏi (1) không thực sự phù hợp với nhau, trừ khi tôi đang đọc sai điều gì đó. Tuy nhiên, 1
Dennis Zickefoose

1
@Xeo. Cảm ơn bạn đã đóng góp ý kiến. Tôi đã chỉnh sửa câu hỏi của mình. STL có sử dụng hoán đổi như bạn đã mô tả trong trường hợp 1 không?
pic11

1
@pic: Có, STL sẽ sử dụng hoán đổi ADL mà tôi đã trình bày trong 1), nhưng chỉ khi nó ở đó như một chức năng miễn phí, không chỉ một chức năng thành viên. Xem 2) và 3), cả hai phiên bản sẽ được chọn bởi các thuật toán. Tôi muốn lời khuyên 2), như 3) đã lỗi thời và bị coi là thực hành xấu.
Xeo

2
Nhận xét trong đoạn mã đầu tiên là sai lệch. using std::swap;không kích hoạt ADL, nó chỉ cho phép trình biên dịch xác định vị trí std::swapnếu ADL không tìm thấy quá tải thích hợp.
David Rodríguez - dribeas

6
Câu trả lời này đúng về mặt kỹ thuật, nhưng rất cần được chỉnh sửa cho rõ ràng. OP's (1) không phải là câu trả lời chính xác vì cách đọc quá nhanh trong câu trả lời này có vẻ như chỉ ra một cách nhầm lẫn.
Howard Hinnant

1

Để trả lời EDIT, trong đó các lớp có thể là các lớp mẫu, bạn không cần chuyên môn hóa gì cả. hãy xem xét một lớp học như thế này:

template <class T>
struct vec3
{
    T x,y,z;
};

bạn có thể xác định các lớp như:

vec3<float> a;
vec3<double> b;
vec3<int> c;

nếu bạn muốn có thể tạo một hàm để thực hiện cả 3 hoán đổi (không phải lớp ví dụ này đảm bảo điều đó), bạn làm giống như Xeo đã nói trong (2) ... mà không cần chuyên môn hóa mà chỉ cần tạo một hàm mẫu thông thường:

template <class T>
void swap(vec3<T> &a, vec3<T> &b)
{
    using std::swap;
    swap(a.x,b.x);
    swap(a.y,b.y);
    swap(a.z,b.z);
}

Hàm mẫu trao đổi phải được đặt trong cùng một vùng tên với lớp mà bạn đang cố gắng hoán đổi. phương pháp sau sẽ tìm và sử dụng hoán đổi đó ngay cả khi bạn không tham chiếu đến không gian tên đó bằng ADL:

using std::swap;
swap(a,b);

0

Có vẻ như (2) ( đứng tự do swaptrong cùng một không gian tên nơi khai báo lớp do người dùng xác định ) là cách được phép duy nhất để cung cấp swapcho một lớp do người dùng xác định, bởi vì việc thêm khai báo vào không gian tên stdnói chung là một hành vi không xác định. Mở rộng không gian tên std (cppreference.com) :

Việc thêm khai báo hoặc định nghĩa vào không gian tên stdhoặc bất kỳ không gian tên nào được lồng trong đó là hành vi không xác định std, với một vài ngoại lệ được lưu ý dưới đây

swapkhông được biểu thị là một trong những trường hợp ngoại lệ. Vì vậy, thêm swapquá tải của riêng bạn vàostd không gian tên là một hành vi không xác định.

Người ta cũng nói rằng thư viện chuẩn sử dụng một lệnh gọi không đủ tiêu chuẩn tới swaphàm để gọi do người dùng xác định swapcho một lớp người dùng nếu người dùng đó xác địnhswap được cung cấp.

Có thể thay đổi (cppreference.com) :

Nhiều hàm thư viện tiêu chuẩn (ví dụ, nhiều thuật toán) mong đợi các đối số của chúng đáp ứng Swappable , có nghĩa là bất kỳ lúc nào thư viện chuẩn thực hiện hoán đổi, nó sử dụng tương đương vớiusing std::swap; swap(t, u); .

hoán đổi (www.cplusplus.com) :

Nhiều thành phần của thư viện chuẩn (trong std) gọi swaptrong một không đủ tiêu chuẩn theo cách để cho phép quá tải tùy chỉnh với nhiều loại phi cơ bản được gọi là thay vì phiên bản generic này: quá tải Tuỳ chỉnh của swaptuyên bố trong không gian tên tương tự như các loại mà họ được cung cấp được chọn thông qua tra cứu phụ thuộc vào đối số trên phiên bản chung này.

Nhưng lưu ý rằng việc sử dụng trực tiếp std::swaphàm cho lớp do người dùng xác định sẽ gọi phiên bản chung std::swapthay vì người dùng xác định swap:

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

Vì vậy, bạn nên gọi swaphàm trong mã người dùng theo cách giống như cách thực hiện trong thư viện chuẩn:

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
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.