Tôi có một lớp giống như vùng chứa, mà tôi muốn di chuyển chỉ khi kiểu cơ bản là chỉ di chuyển, nhưng có thể sao chép được. Để đơn giản, chúng ta hãy giả copyability được xác định bởi một đơn bool mẫu tham số:Thêm hàm tạo bản sao dựa trên thông số mẫu
template<bool IsCopyable>
struct Foo
{
Foo();
Foo(const Foo&); // only include this when IsCopyable is true
Foo(Foo&&);
Foo& operator=(const Foo&); // only when IsCopyable
Foo& operator=(Foo&&);
};
Bây giờ, tôi không thể chỉ SFINAE bản sao ctor đi, bởi vì đó đòi hỏi làm cho nó templated và một chức năng templated không thể là một ctor bản sao. Ngoài ra, tôi không thể chỉ làm một static_assert()
trong ctor sao chép. Mặc dù điều này sẽ bắt gặp việc sử dụng sai lầm của ctor bản sao, nó cũng làm cho lớp vốn đã sao chép có thể xây dựng từ bên ngoài (đặc điểm loại std::is_copy_constructible
sẽ mang lại giá trị đúng). Bằng cách này, một yêu cầu không may là nó cần phải biên dịch trong VC++ 2012, vì vậy tôi không thể sử dụng biểu thức ưa thích SFINAE, hàm ctors thừa kế, hàm mặc định/đã xóa hoặc constexpr if
(tuy nhiên, nếu bạn có một giải pháp gọn gàng cho C++ 17 Tôi vẫn muốn nghe nó :))
Phương pháp hiển nhiên là sử dụng chuyên môn mẫu. Tôi không muốn đi tuyến đường này, bởi vì trong thực tế Foo có khá nhiều chức năng và tôi không muốn lặp lại bản thân mình. Tuy nhiên, điều này có vẻ là lựa chọn duy nhất của tôi và tôi có thể triển khai một số chia sẻ mã bằng cách sử dụng lớp cơ sở, như vậy:
// Base functionality
template<bool IsCopyable>
struct FooBase
{
FooBase();
// move ctor and assignment can go here
FooBase(FooBase&&);
FooBase& operator=(FooBase&&);
// some generic conversion ctor and assignment that I happen to need
template<class T> FooBase(T&& t);
template<class T> FooBase& operator=(T&&);
// ... all sorts of functionality and datamembers
};
// Foo<false>
template<bool IsCopyable>
struct Foo : FooBase<IsCopyable>
{
// can't use inheriting ctors in VS 2012, wrap the calls manually:
Foo() { }
Foo(Foo&& other) : FooBase<IsCopyable>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<IsCopyable>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<IsCopyable>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<IsCopyable>::operator=(std::forward<T>(t));
return *this;
}
};
// Foo<true>
template<>
struct Foo<true> : FooBase<true>
{
// add these
Foo(const Foo&);
Foo& operator=(const Foo&);
// wrapping calls because of VS 2012:
Foo() { }
Foo(Foo&& other) : FooBase<true>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<true>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<true>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<true>::operator=(std::forward<T>(t));
return *this;
}
};
Vẫn còn một chút. May mắn thay nó được sạch hơn một khi bạn có thể sử dụng ctors kế thừa và chức năng mặc định. Tuy nhiên, tôi đã hy vọng có một cách đơn giản hơn, lý tưởng là không sử dụng một lớp cơ sở.
Wow! Đơn giản và thanh lịch. Tôi phải nhớ thủ thuật này. – max66
Giải pháp tuyệt vời ngoại trừ việc thành ngữ "không có định nghĩa riêng" thành ngữ đã lỗi thời trong C++ 11. Tôi đã xóa những phương pháp này một cách rõ ràng. –
@ArneVogel OP cho biết họ không thể sử dụng các chức năng đã xóa. –