2017-02-20 23 views
5

Có bao giờ có ý nghĩa khi có một hàm tạo di chuyển constexpr?Có một constructor constexpr di chuyển không?

Ví dụ, hãy xem xét những điều sau đây:

#include <array> 

class C 
{ 
public: 
    constexpr C(std::array<int, 3> ar) : m_ar{ar} {} 
    constexpr C(C&& other) : m_ar{std::move(other.m_ar)} { } 
private: 
    std::array<int, 3> m_ar; 
}; 

int main() 
{ 
    constexpr C c1 {{{1, 2, 3}}}; 
    constexpr C c2{std::move(c1)}; 
    return 0; 
} 

này không biên dịch, vì mặc dù gọi std::move trên c1, trình biên dịch deduces nó cần phải sử dụng các nhà xây dựng (ngầm xóa) sao chép, không phải là nhà xây dựng di chuyển . Tôi cung không chăc tại sao.

Nhưng nếu tôi xóa constexpr từ c1 thì nó sẽ không thể sử dụng được bởi hàm tạo di chuyển constexpr.

Có cách nào để làm việc này không? Hay đây là một ví dụ xấu cho một hàm tạo di chuyển constexpr, nhưng có những ví dụ hay? Hoặc, nó chỉ luôn luôn sai khi có một constructor di chuyển constexpr?

+0

Với 'constexpr C (const C && other)', nó biên dịch từ C++ 14. – Jarod42

+0

@ Jarod42 Nhưng nó có khả năng không thực sự di chuyển bất cứ điều gì, vì bạn không thể di chuyển từ một đối tượng const. Nó trở thành một hàm tạo bản sao phức tạp không cần thiết. –

+0

@ Jarod42 Huh. Nó có. Làm thế nào mà? Tôi mong đợi 'std :: move (other.m_ar)' thất bại vì 'other' là' const C && '(vì nó biên dịch, tôi mong đợi nó * làm * di chuyển' khác.m_ar', trừ khi nó làm một cái gì đó lén lút thay vì như sao chép 'other.m_ar' thay thế). – Danra

Trả lời

2

Về nguyên tắc, một constructor di chuyển có thể được sử dụng với một đối tượng phi const mà cuộc đời bắt đầu trong việc đánh giá các biểu thức hằng:

// C++14 
constexpr int f() { 
    C a(/* ... */); 
    C b = std::move(a); 
    return 0; 
} 
constexpr int i = f(); 

điều tương tự có thể được thực hiện trong C++ 11, ví dụ

constexpr C foo(C&& c) { 
    return std::move(c); 
} 

constexpr int f() { 
    return foo(C()), 0; 
} 

Điều đó nói rằng, vì mọi thứ được sử dụng trong biểu thức liên tục bắt buộc phải bị phá hủy tầm thường, tính hữu ích của hàm tạo di chuyển khá hạn chế.

+0

Thú vị! Tôi đoán điều này có nghĩa là các nhà thầu di chuyển có thể trở nên hữu ích hơn trong tương lai khi (nếu?) Các trình phá hủy không tầm thường được cho phép trong các biểu thức constexpr. Có bất kỳ ví dụ mà đến tâm trí mà nó * có thể * có ích cho một lớp học với chỉ là một destruct tầm thường? – Danra

2

này không biên dịch, vì mặc dù gọi std::move trên c1, trình biên dịch deduces nó cần phải sử dụng (mặc nhiên bị xóa) copy constructor

c1 là loại C const. Khi bạn move(), đó thực sự là tham chiếu để tham khảo rvalue, vì vậy bạn nhận được C const&&. Lưu ý rằng nó vẫn là const. Khi chúng tôi thực hiện nghị quyết tình trạng quá tải, có ba cấu trúc:

C(std::array<int, 3>); // not viable 
C(C&&);    // not viable 
C(C const&) = delete; // viable! 

C const&& không thể liên kết với C&& với lý do tương tự mà C const& không thể liên kết với C&. Chúng ta chỉ còn lại với hàm tạo bản sao, được xóa hoàn toàn.


Có một constructor chuyển constexpr có thể có ý nghĩa - nhưng đối tượng bạn đang "di chuyển" từ không thể thực sự được chuyển từ, bởi vì nó có lẽ const. Bạn có thể thêm một constructor const di chuyển:

constexpr C(C const&& other) : m_ar(other.m_ar) { } 

Đây là một constructor sao chép được vinh hiển, nhưng nó cho phép cú pháp mà bạn muốn. Có thể cũng chỉ cho phép sao chép.

+0

Điều đó có thể có nghĩa là hàm khởi tạo của tôi ở trên trở nên lén lút nếu tôi tạo tham số 'const &&' nhưng giữ mã' std :: move (other.m_ar) ', vì nó sẽ sao chép mảng, chứ không di chuyển nó. – Danra

+0

Trong mọi trường hợp mục đích không phải là để làm cho cú pháp của tôi hoạt động, điểm lớn hơn là nếu nó có ý nghĩa để thực sự có một ctor di chuyển constexpr. – Danra

+0

@Danra Không sai. Di chuyển không * có * là một cái gì đó khác hơn là sao chép. – Barry

Các vấn đề liên quan