2013-06-10 27 views
10

Tôi có một mẫu như thế này:std :: di chuyển trong danh sách initializer constructor trong lớp mẫu

template<typename T> 
struct foo { 
    T m_t; 
    foo(T t) : m_t(t) {} 
}; 

Vấn đề là tôi muốn hỗ trợ cả hai/loại thường nhỏ và khổng lồ loại (như ma trận) cho T. Bạn có khuyên tôi viết danh sách khởi tạo của hàm dựng như thế này

foo (T t) : m_t(std::move(t)) {} 

và yêu cầu loại T luôn hỗ trợ xây dựng di chuyển, ngay cả đối với các loại nhỏ hơn? Có cách nào tốt hơn không?

Trả lời

11

và yêu cầu loại T luôn hỗ trợ xây dựng di chuyển, ngay cả đối với các loại nhỏ hơn?

Bất kỳ loại nào là bản sao có thể xây dựng cũng có thể di chuyển được. Di chuyển trong những trường hợp đó chỉ đơn giản là gọi hàm tạo bản sao. Do đó, không có lý do nào không phải để sử dụng m_t(std::move(t)).

Một cách khác là sử dụng tài liệu tham khảo thay vì:

foo (T const& t) : m_t(t) {} 
foo (T&& t) : m_t(std::move(t)) {} 

này có lợi ích của chỉ liên quan đến một trong xây dựng chứ không phải là hai.

+0

Cách tham khảo của bạn có tốt hơn tôi không? Nếu T không di chuyển có thể xây dựng được thì (hoặc nên) ngụ ý rằng T là nhỏ/bình thường (không lớn), phải không? Trong trường hợp này bạn có nghĩ rằng một tham chiếu mang lại cho tôi bất kỳ lợi ích hiệu suất có thể đo lường nào không? – 7cows

+0

@ 7có thể mang lại lợi ích hiệu suất có thể đo lường khi T có một hàm tạo bản sao đắt tiền và không có hàm tạo di chuyển nào, thường là với các thư viện C++ 03. Tôi thường thường thích theo cách của bạn. – Pubby

7

Có, sử dụng di chuyển không có bất lợi trong tình huống đó. Tất cả các đối tượng có thể sao chép được tự động di chuyển, vì vậy nó không quan trọng. Trong thực tế, một số người khuyên bạn nên luôn luôn di chuyển các biến khi có thể, ngay cả số nguyên.

Là một thay thế, bạn có thể xem xét sử dụng chuyển tiếp hoàn hảo, như mô tả trong this answer:

template <typename T2> 
foo(T2&& t) : m_t(std::forward<T2>(t)) {} 

Nếu bạn biết rằng T định nghĩa một constructor di chuyển nhanh, nó không phải vấn đề. Nếu không, việc cung cấp một hàm tạo foo(const T&) được khuyến nghị để tránh các bản sao không cần thiết.

Chuyển tiếp hoàn hảo chỉ là một kỹ thuật để đạt được điều đó. Các giải pháp của Pubby để viết ra các nhà xây dựng foo(const T&)foo(T&&) là, tất nhiên, cũng tốt. Kết quả là như nhau, nó chủ yếu là vấn đề về phong cách.

Bạn cũng đã hỏi về các loại số nguyên nhỏ. Theo lý thuyết, việc chuyển chúng bằng tham chiếu chậm hơn so với việc sao chép chúng, nhưng trình biên dịch sẽ có thể tối ưu hóa nó thành một bản sao. Tôi không nghĩ nó sẽ tạo nên sự khác biệt.

Vì vậy, tối ưu hóa tốt hơn cho trường hợp xấu nhất, nơi T có thể rất lớn và không cung cấp hàm khởi tạo nhanh. Đi qua tham chiếu là tốt nhất cho tình huống đó và cũng không phải là một lựa chọn tồi.

+2

Vấn đề với việc chuyển tiếp hoàn hảo là nó sẽ chấp nhận * mọi thứ * mà hàm tạo 'T' chấp nhận. – Pubby

+0

@Pubby Điểm tốt. Điều đó có thể dương hoặc âm, tùy thuộc vào ngữ cảnh. Nếu cần nhiều quyền kiểm soát hơn đối với chuyển đổi, cả hai hàm tạo 'foo' có thể được khai báo là' tường minh'. –

+0

@Pubby, có thể được kiểm soát bằng 'enable_if' và kiểm tra' is_same'. Cấp, vào thời điểm bạn nhận được rằng bằng văn bản, bạn có thể nhìn vào cùng một số dòng như rõ ràng thực hiện cả hai nhà xây dựng ... –

0

Việc truyền theo giá trị có lợi thế là chỉ có một hàm tạo đơn (không có mẫu), nhưng có giá bằng một công trình di chuyển bổ sung so với các phương án thay thế nói trên.

Tuy nhiên, có một nhược điểm khác chưa được đề cập của cả hai giá trị trả theo giá trị và không phải mẫu do Pubby cung cấp: Di chuyển sẽ không hoạt động nếu hàm tạo bản sao được định nghĩa là T(T&); (lưu ý tham chiếu đến không const).Các tham chiếu Rvalue có thể liên kết với tham chiếu lvalue-to-const, nhưng không liên kết với tham chiếu lvalue-to-non-const, do đó trình biên dịch không thể gọi hàm tạo bản sao.

Để giải quyết vấn đề này, bạn chỉ cần thêm quá tải thứ ba foo (T & t) : m_t(t) {} vào giải pháp của Pubby.

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