2011-08-16 38 views
11

Tôi đang cố gắng để đưa các thành ngữ sao chép và hoán đổi thành một mixin tái sử dụng:tái sử dụng sao chép và hoán đổi thành ngữ

template<typename Derived> 
struct copy_and_swap 
{ 
    Derived& operator=(Derived copy) 
    { 
     Derived* derived = static_cast<Derived*>(this); 
     derived->swap(copy); 
     return *derived; 
    } 
}; 

tôi dự định nó được trộn lẫn trong qua CRTP:

struct Foo : copy_and_swap<Foo> 
{ 
    Foo() 
    { 
     std::cout << "default\n"; 
    } 

    Foo(const Foo& other) 
    { 
     std::cout << "copy\n"; 
    } 

    void swap(Foo& other) 
    { 
     std::cout << "swap\n"; 
    } 
}; 

Tuy nhiên, một thử nghiệm đơn giản cho thấy rằng nó không phải đang làm việc:

Foo x; 
Foo y; 
x = y; 

chỉ in "mặc định" này hai lần, không phải "sao chép" hay "hoán đổi" được in ra. Tôi đang thiếu gì ở đây?

+1

lẽ trình biên dịch được cung cấp phiên bản của riêng mình của 'operator = 'kể từ khi bạn là thiếu một trong lớp 'Foo' của bạn? Có lẽ bạn sẽ phải làm 'Foo :: operator =() {return copy_and_swap();}' (mã giả)? – RedX

Trả lời

0

Tôi e rằng đây là một lĩnh vực cần macro, vì các quy tắc phức tạp về các toán tử gán và sao chép được tạo tự động.

Không có vấn đề gì bạn làm, bạn đang ở trong một trong hai trường hợp:

  • Bạn đã cung cấp (rõ ràng) một tuyên bố của các nhà điều hành chuyển nhượng, trong trường hợp mà bạn đang mong đợi để cung cấp một định nghĩa quá
  • Bạn chưa cung cấp (một cách rõ ràng) một khai báo của toán tử gán, trong trường hợp đó trình biên dịch sẽ tạo ra một nếu các lớp cơ sở các thành viên không tĩnh có sẵn.

Câu hỏi tiếp theo, là: Có đáng để tự động viết như vậy không?

Sao chép-và-Hoán đổi chỉ được sử dụng cho các lớp học rất cụ thể. Tôi không nghĩ nó xứng đáng.

1

Bạn không thể kế thừa các toán tử gán như một trường hợp đặc biệt, nếu bộ nhớ phục vụ đúng cách. Tôi tin rằng họ có thể rõ ràng là using nếu bạn cần.

Ngoài ra, hãy cẩn thận về việc sử dụng sao chép và hoán đổi. Nó tạo ra kết quả không lý tưởng, nơi bản gốc có các tài nguyên có thể được tái sử dụng để tạo bản sao, chẳng hạn như các thùng chứa. An toàn được đảm bảo nhưng hiệu suất tối ưu thì không.

+0

Thêm toán tử 'using copy_and_swap :: =;' vào 'struct Foo', không sửa tình huống. Xem [Câu trả lời của Alexandre C.] (http://stackoverflow.com/questions/7080137/reusing-the-copy-and-swap-idiom/7080212#7080212). –

7

này:

Derived& operator=(Derived copy) 

không khai báo một toán tử gán bản sao cho các lớp cơ sở (nó có chữ ký sai). Vì vậy, toán tử gán được tạo mặc định trong Foo sẽ không sử dụng toán tử này.

Ghi 12,8:

Một người dùng tuyên bố nhiệm vụ sao chép điều hành X :: operator = là một tổ chức phi tĩnh phi mẫu hàm thành viên của lớp X với chính xác một tham số của loại X, X & , const X &, dễ bay hơi X & hoặc const dễ bay hơi X &.) [Ghi chú: toán tử gán quá tải phải được khai báo chỉ có một thông số ; xem 13.5.3. ] [Lưu ý: nhiều hơn một hình thức chuyển nhượng bản sao nhà điều hành có thể được khai báo cho một lớp học. ] [Chú ý: nếu một lớp X chỉ có một toán tử gán bản sao với một tham số kiểu X &, một biểu hiện của loại const X không thể được gán cho một đối tượng kiểu X.

EDIT don 't làm điều này (bạn có thể thấy tại sao?):

bạn có thể làm:

template<typename Derived> 
struct copy_and_swap 
{ 
    void operator=(const copy_and_swap& copy) 
    { 
     Derived copy(static_cast<const Derived&>(copy)); 
     copy.swap(static_cast<Derived&>(*this)); 
    } 
}; 

nhưng bạn bị mất việc tối ưu hóa sao chép sự bỏ bớt tiềm năng.

Thật vậy, điều này sẽ chỉ định hai lần các thành viên của các lớp dẫn xuất: một lần thông qua nhà điều hành gán copy_and_swap<Derived> và một lần thông qua toán tử gán được tạo ra của lớp dẫn xuất.Để khắc phục tình trạng này, bạn sẽ phải làm (và không quên làm):

struct Foo : copy_and_swap<Foo> 
{ 

    Foo& operator=(const Foo& x) 
    { 
     static_cast<copy_and_swap<Foo>&>(*this) = x; 
     return *this; 
    } 

private: 
    // Some stateful members here 
} 

Các đạo đức của câu chuyện: không viết một lớp CRTP cho sao chép và hoán đổi thành ngữ.

+0

Nếu bạn '= xóa' toán tử gán trình biên dịch tạo ra cho Foo (giả sử C++ 0x) thì sao? –

+0

'= delete' sẽ cấm gọi' operator = 'trên Foo, điều này phản tác dụng với điều này. –

+0

Bạn có đang cắt sự cố dẫn đến chỉnh sửa của mình không? –

0

Trình biên dịch sẽ tự động tạo toán tử gán bản sao cho Foo, vì không có. Nếu bạn thêm một

using copy_and_swap<Foo>::operator=; 

để Foo bạn sẽ thấy một lỗi nói cho bạn về sự nhập nhằng về g ++.

+0

Tôi đã thử điều này và nó không cung cấp cho bất kỳ sự mơ hồ. Tuy nhiên, toán tử vẫn chưa được gọi. –

+0

@ André Caron: Tôi đã kiểm tra lại: g ++ không msvc không. – mmmmmmmm

0

Có lẽ bạn có thể viết lại nó để nó trông giống như vậy:

template<class Derived> 
struct CopySwap 
{ 
    Dervied &operator=(Derived const &other) 
    { 
    return AssignImpl(other); 
    } 

    Derived &operator=(Dervied &&other) 
    { 
    return AssignImpl(std::move(other)); 
    } 

private: 
    Derived &AssignImpl(Derived other) 
    { 
    auto self(static_cast<Derived*>(this)); 
    self->swap(other); 
    return *self; 
    } 
}; 

Nó sẽ có lẽ tất cả được inlined và có khả năng sẽ không có bất kỳ chậm hơn so với mã gốc.

0

này không thực sự trả lời câu hỏi (@Alexandre C. already did), nhưng nếu bạn đảo ngược thừa kế, bạn có thể làm cho nó hoạt động:

template<typename Base> 
struct copy_and_swap : Base 
{ 
    copy_and_swap& operator=(copy_and_swap copy) 
    { 
     swap(copy); 
     return *this; 
    } 
}; 

struct Foo_ 
{ 
    Foo_() 
    { 
     std::cout << "default\n"; 
    } 

    Foo_(const Foo_& other) 
    { 
     std::cout << "copy\n"; 
    } 

    void swap(Foo_& other) 
    { 
     std::cout << "swap\n"; 
    } 
}; 

typedef copy_and_swap<Foo_> Foo; 

int main() 
{ 
    Foo x; 
    Foo y; 
    x = y; 
} 
Các vấn đề liên quan