2016-03-11 54 views
16

Tôi đang xem xét tối ưu hóa giá trị trả về trong trường hợp tuple/ties và hành vi tôi quan sát không như tôi mong đợi. Trong ví dụ dưới đây, tôi sẽ mong đợi di chuyển ngữ nghĩa để đá vào, mà nó làm, nhưng có một hoạt động sao chép mà vẫn còn. Đầu ra từ bên dưới được tối ưu hóa là:Tối ưu hóa giá trị trả về của tuple/tie

Test duo output, non_reference tuple 
Default constructor invoked 
Parameter constructor invoked 
Copy constructor invoked 
Move Assignment operator invoked 
100 

Việc yêu cầu người tạo bản sao làm cho bộ túp bên trong hàm có vẻ không cần thiết. Có cách nào để loại bỏ điều này không? Tôi đang sử dụng trình biên dịch MSVC 2012.

#include <iostream> 
#include <tuple> 

class A 
{ 
public: 
    int value; 
    A() : value(-1) 
    { 
     std::cout << "Default constructor invoked" << std::endl; 
    } 

    explicit A(const int v) : value(v) 
    { 
     std::cout << "Parameter constructor invoked" << std::endl; 
    } 

    A(const A& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Copy constructor invoked" << std::endl; 
    } 

    A(const A&& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Move constructor invoked" << std::endl; 
    } 

    A& operator=(const A& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Assignment operator invoked" << std::endl; 
     return *this; 
    } 

    A& operator=(const A&& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Move Assignment operator invoked" << std::endl; 
     return *this; 
    } 
}; 

std::tuple<A, int> return_two_non_reference_tuple() 
{ 
    A tmp(100); 

    return std::make_tuple(tmp, 99); 
} 

int main(int argc, char* argv[]) 
{ 

     std::cout << "Test duo output, non_reference tuple" << std::endl;  
     A t3; 
     int v1; 
     std::tie(t3, v1) = return_two_non_reference_tuple(); 
     std::cout << t3.value << std::endl << std::endl; 

     system("pause"); 
     return 0; 
} 

Trả lời

6

Các nhà xây dựng di chuyển sẽ không được gọi là tự động vì bạn đang gọi

std::make_tuple(tmp, 99); 

Trong trường hợp này, tmp là một giá trị. Bạn có thể sử dụng std::move bỏ nó vào một tài liệu tham khảo rvalue:

return std::make_tuple(std::move(tmp), 99); 

này sẽ hướng dẫn các trình biên dịch sử dụng các nhà xây dựng di chuyển.

1

Bạn đang không di chuyển tmp:

A tmp(100); 

return std::make_tuple(std::move(tmp), 99); 
3

Bản sao diễn ra trong return_two_non_reference_tuple().

Một cách để loại bỏ nó là để di chuyển tmp

std::tuple<A, int> return_two_non_reference_tuple() 
{ 
    A tmp(100); 
    return std::make_tuple(std::move(tmp), 99); 
} 

hoặc một cách khác

std::tuple<A, int> return_two_non_reference_tuple() 
{ 
    return std::make_tuple(A(100), 99); 
} 
3

Bản sao xảy ra ở đây:

std::make_tuple(tmp, 99); 

Mặc dù bạn có thể thấy rằng tmp có thể có thể được xây dựng trực tiếp trong tuple, bản sao từ tmp đến tuple sẽ không được elided. Những gì bạn thực sự muốn là một cách để vượt qua trong các đối số cho std::tuple để sử dụng để xây dựng các đối tượng nội bộ Aint của nó. Không có một điều như vậy cho std::tuple, nhưng có một cách để đạt được hiệu quả tương tự.

Vì bạn chỉ có hai loại, bạn có thể sử dụng std::pair. Điều này có một constructor std::piecewise_construct, trong đó có hai std::tuples chứa các đối số để truyền cho các hàm tạo của các đối tượng bên trong.

std::pair<A, int> return_two_non_reference_tuple() 
{ 
    return {std::piecewise_construct, 
      std::make_tuple(100), std::make_tuple(99)}; 
} 

Điều thú vị về giải pháp này là bạn vẫn có thể sử dụng std::tie tại địa điểm cuộc gọi, vì std::tuple có một toán tử gán từ std::pair.

std::tie(t3, v1) = return_two_non_reference_tuple(); 

Như bạn thấy từ đầu ra, bản sao của bạn đã biến mất.Nó không phải được thay thế bằng một động thái như trong câu trả lời khác, nó hoàn toàn loại bỏ:

thử nghiệm bộ đôi sản lượng, tuple non_reference

Mặc định constructor gọi

Parameter constructor gọi

Move Chuyển nhượng toán tử được gọi

Live Demo

+0

Cảm ơn, đây là lựa chọn tốt cho trường hợp đơn giản, nhưng nếu có nhiều hành động hơn trên tmp và nhiều đầu ra hơn, nó sẽ không hoạt động. Và tôi không nghĩ rằng nó sẽ làm việc trong MSVC 2012. – thorsan

+0

Sao chép elision đang xảy ra ở đây. Vấn đề là 'tmp' không được chuyển vào' tuple'. Các nhà xây dựng bản sao là từ sao chép 'tmp' vào tuple, không phải là tuple được sao chép. – Simple

+0

@Simple Tôi biết rằng, có lẽ câu trả lời của tôi không đủ rõ ràng. – TartanLlama

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