2011-12-23 11 views
32

thể trùng lặp:
Move semantics == custom swap function obsolete?Chuyên std :: hoán đổi không được chấp nhận bây giờ mà chúng tôi đã di chuyển ngữ nghĩa?

Đây là cách std::swap trông giống như trong C++ 11:

template<typename T> 
void swap(T& x, T& y) 
{ 
    T z = std::move(x); 
    x = std::move(y); 
    y = std::move(z); 
} 

Tôi vẫn phải chuyên std::swap với nhiều loại của riêng tôi , hoặc sẽ std::swap hoạt động hiệu quả, miễn là lớp học của tôi xác định ove constructor và một nhà điều hành chuyển nhượng, tất nhiên?

+0

Tôi muốn nói điều này đã được đề cập bởi Scott hoặc Herb tại _C++ và Beyond_, nhưng tôi dường như không thể tìm thấy bất cứ điều gì về nó. –

+2

Liên quan: [Di chuyển ngữ nghĩa == chức năng hoán đổi tùy chỉnh lỗi thời?] (Http://stackoverflow.com/questions/6416385/move-semantics-custom-swap-function-obsolete) – Xeo

+0

@Xeo: Cảm ơn, tôi đã hoàn toàn quên mất câu hỏi/câu trả lời đó. Tôi có được điểm cho phù hợp không? :-) Tôi chắc chắn sẽ bị đỏ mặt khác! –

Trả lời

30

Sự chuyên môn của std::swap bây giờ là không bắt buộc, nhưng không phản đối. Lý do là hiệu suất.

Để tạo mã mẫu và thậm chí có thể cho nhiều mã vận chuyển, std::swap sẽ nhanh hơn rất nhiều. Tuy nhiên nếu bạn đang ở trong một tình huống mà bạn cần phải eek ra mỗi chút từ mã của bạn, viết một trao đổi tùy chỉnh vẫn có thể là một lợi thế hiệu suất đáng kể.

Hãy xem xét trường hợp lớp của bạn về cơ bản có một con trỏ đang sở hữu và hàm tạo di chuyển của bạn và nhiệm vụ di chuyển chỉ phải đối phó với một con trỏ đó.Đếm tải và lưu trữ máy cho mỗi thành viên:

Di chuyển hàm khởi tạo: 1 tải và 2 cửa hàng.

Di chuyển bài tập: 2 tải và 2 cửa hàng.

Hoán đổi tùy chỉnh: 2 lượt tải và 2 cửa hàng.

std::swap là 1 lần di chuyển và 2 bài tập di chuyển hoặc: 5 lượt tải và 6 cửa hàng.

Hoán đổi tùy chỉnh có khả năng vẫn nhanh hơn hai hoặc ba lần so với std::swap. Mặc dù bất cứ lúc nào bạn đang cố gắng tìm ra tốc độ của một cái gì đó bằng cách đếm tải và các cửa hàng, cả hai sẽ được nhanh chóng ác.

Lưu ý: Khi tính toán chi phí chuyển nhượng di chuyển của bạn, hãy đảm bảo và xem xét rằng bạn sẽ chuyển sang giá trị được chuyển từ (trong thuật toán std::swap). Điều này thường phủ nhận chi phí của một deallocation, mặc dù với chi phí của một chi nhánh.

+7

Không nên để trình biên dịch có thể chuyển nội tuyến bài tập di chuyển và xóa các cửa hàng chết? – u0b34a0f6ae

+7

Tôi không phải là kỹ sư biên dịch. Tôi đoán là có thể, nhưng tôi không biết chắc chắn. Tôi sẽ do dự để phụ thuộc vào nó. Vào cuối ngày, bạn sẽ phải mã hóa cả hai cách và kiểm tra. Và nếu thử nghiệm của bạn chỉ ra rằng 'std :: swap' chỉ là nhanh, kết quả đó cũng có thể phụ thuộc vào trình biên dịch/nền tảng. Và nếu ứng dụng của bạn không 'hoán đổi' trong một vùng hiệu năng quan trọng, thì có thể bạn sẽ không quan tâm nếu' std :: swap' gấp hai lần chậm chạp như vậy. –

+1

Đến một chút muộn để các bên ... Nhưng tôi có một nghi ngờ đơn giản: tại sao Move Assignment chiếm 2Loads + 2Stores (1 tải nhiều hơn Move Constructor)? Có phải vì nó phải làm "trả lại * cái này" không? Hoặc vì nhu cầu ngụ ý để "xóa" con trỏ sở hữu trước khi gán lại nó, và vì vậy nó phải được đọc trong biểu thức xóa? – abigagli

0

Điều đó sẽ tùy thuộc vào loại của bạn.

Bạn sẽ di chuyển từ x sang z, từ y sang x, từ z đến y. Đó là ba hoạt động sao chép của biểu diễn bên dưới (có thể chỉ một con trỏ, có thể là một cái gì đó nhiều hơn, ai biết)

Bây giờ bạn có thể tạo một hoán đổi nhanh hơn cho loại của bạn (xor swap trick, inline assembler, hoặc có thể std :: trao đổi cho các loại cơ bản của bạn chỉ nhanh hơn).

Hoặc cũng có thể trình biên dịch của bạn tối ưu hóa tốt, và về cơ bản tối ưu hóa cả hai trường hợp vào cùng một hướng dẫn (như có tạm thời trong sổ đăng ký).

Cá nhân tôi có xu hướng luôn triển khai chức năng thành viên trao đổi sẽ được gọi từ nhiều nơi, bao gồm những thứ như chuyển bài tập, nhưng YMMV.

0

Điều này swap() gọi hàm khởi tạo và 2 bài tập di chuyển. Tôi nghĩ rằng người ta có thể viết hiệu quả hơn swap() cho loại hình đặc biệt của mình của lớp như thế nào,

class X 
{ 
    int * ptr_to_huge_array; 
public: 
// ctors, assgn ops. etc. etc. 

    friend void swap(X& a, X& b) 
    { 
     using std::swap; 
     swap(a.ptr_to_huge_array, b.ptr_to_huge_array); 
    } 
}; 

không phụ thuộc vào việc thực hiện các constructor di chuyển và toán tử gán.

+0

Trình tạo bản sao ở đâu? Bạn có thể đưa ra ví dụ về một trao đổi hiệu quả hơn không? – ronag

+0

@ronag: Xin lỗi, lỗi của tôi. –

2

Chuyên std :: hoán đổi không được chấp nhận ngay bây giờ khi chúng tôi di chuyển ngữ nghĩa?

No. Đây là phiên bản chung, nhưng bạn có thể tối ưu hóa nó để bỏ qua thao tác di chuyển thứ ba. Sở thích của tôi là kết hợp copy & thành ngữ hoán đổi với tùy biến std :: swap cho các lớp của tôi.

Điều đó có nghĩa tôi sẽ phải:

class Aaaa 
{ 
public: 
    Aaaa(); // not interesting; defined elsewhere 
    Aaaa(Aaaa&& rvalueRef); // same 
    Aaaa(const Aaaa& ref); // same 
    ~Aaaa(); // same 
    Aaaa& operator=(Aaaa object) // copy&swap 
    { 
     swap(object); 
     return *this; 
    } 
    void swap(Aaaa& other) 
    { 
     std::swap(dataMember1, other.dataMember1); 
     std::swap(dataMember2, other.dataMember2); 
     // ... 
    } 

    // ... 
}; 

namespace std 
{ 
    template<> inline void std::swap(Aaaa& left, Aaaa& right) 
    { left.swap(right); } 
} 
+3

Cách chính xác để sử dụng 'hoán đổi' là' sử dụng std :: swap' theo sau là một cuộc gọi không đủ tiêu chuẩn để 'hoán đổi'. Ngoài ra, chuyên 'std :: swap' được xem là Not So Good ™, bởi vì bạn không thể thực hiện một phần chức năng. Xem [câu trả lời này của tôi] (http://stackoverflow.com/questions/6380862/how-to-provide-a-swap-function-for-my-class/6380882#6380882) để biết thêm chi tiết. – Xeo

+0

Bạn có được phép thêm thứ gì đó vào không gian tên std không? –

+2

@MichaWiedenmann, bạn được phép thêm chuyên môn mẫu chức năng std (như trong ví dụ trên) – utnapistim