2017-05-03 15 views
61

Tôi đã sử dụng std::tie mà không đưa ra nhiều suy nghĩ về nó. Nó hoạt động nên tôi chỉ chấp nhận rằng:Làm thế nào để std :: tie làm việc?

auto test() 
{ 
    int a, b; 
    std::tie(a, b) = std::make_tuple(2, 3); 
    // a is now 2, b is now 3 
    return a + b; // 5 
} 

Nhưng làm thế nào ma thuật đen công việc này? Tạm thời được tạo ra như thế nào bởi std::tie thay đổi ab? Tôi thấy điều này thú vị hơn vì nó là một tính năng thư viện, không phải là một tính năng ngôn ngữ, vì vậy chắc chắn nó là một cái gì đó chúng ta có thể thực hiện bản thân và hiểu.

Trả lời

94

Để làm rõ khái niệm cốt lõi, hãy giảm khái niệm đó thành ví dụ cơ bản hơn. Mặc dù std::tie rất hữu ích cho các chức năng trở về (một tuple của) nhiều giá trị, chúng ta có thể hiểu nó chỉ tốt với chỉ một giá trị:

int a; 
std::tie(a) = std::make_tuple(24); 
return a; // 24 

Những điều chúng ta cần phải biết để đi tiếp:

  • std::tie tạo và trả về một bộ tham chiếu.
  • std::tuple<int>std::tuple<int&> là 2 lớp hoàn toàn khác nhau, không có kết nối giữa chúng, mà chúng được tạo từ cùng một mẫu, std::tuple.
  • tuple có operator= chấp nhận một tuple của các loại khác nhau (nhưng cùng một số), trong đó mỗi thành viên được phân công cá nhân-từ cppreference:

    template< class... UTypes > 
    tuple& operator=(const tuple<UTypes...>& other); 
    

    (3) Đối với tất cả i, gán std::get<i>(other)-std::get<i>(*this) .

Bước tiếp theo là để thoát khỏi những chức năng mà chỉ nhận được theo cách của bạn, vì vậy chúng tôi có thể chuyển đổi mã của chúng tôi như thế này:

int a; 
std::tuple<int&>{a} = std::tuple<int>{24}; 
return a; // 24 

Bước tiếp theo là để xem chính xác những gì xảy ra bên trong những cấu trúc đó. Đối với điều này, tôi tạo ra 2 loại T nhóm thế cho std::tuple<int>Tr nhóm thế std::tuple<int&>, tước xuống đến mức tối thiểu cho các hoạt động của chúng tôi:

struct T { // substituent for std::tuple<int> 
    int x; 
}; 

struct Tr { // substituent for std::tuple<int&> 
    int& xr; 

    auto operator=(const T& other) 
    { 
     // std::get<I>(*this) = std::get<I>(other); 
     xr = other.x; 
    } 
}; 

auto foo() 
{ 
    int a; 
    Tr{a} = T{24}; 

    return a; // 24 
} 

Và cuối cùng, tôi muốn thoát khỏi các cấu trúc tất cả cùng nhau (tốt, nó không phải là 100% tương đương, nhưng nó đủ gần cho chúng ta, và đủ để cho phép nó rõ ràng):

auto foo() 
{ 
    int a; 

    { // block substituent for temporary variables 

    // Tr{a} 
    int& tr_xr = a; 

    // T{24} 
    int t_x = 24; 

    // = (asignement) 
    tr_xr = t_x; 
    } 

    return a; // 24 
} 

vì vậy, về cơ bản, std::tie(a) khởi tạo một tài liệu tham khảo thành viên dữ liệu để a. std::tuple<int>(24) tạo thành viên dữ liệu có giá trị 24 và gán gán gán 24 cho tham chiếu thành viên dữ liệu trong cấu trúc đầu tiên. Nhưng vì thành viên dữ liệu đó là tham chiếu được ràng buộc với a, về cơ bản chỉ định 24 đến a.

13

Điều này không trả lời câu hỏi của bạn theo bất kỳ cách nào, nhưng hãy để tôi đăng bài vì C++ 17 về cơ bản đã sẵn sàng (với hỗ trợ trình biên dịch), vì vậy trong khi tự hỏi các công cụ đã lỗi thời hoạt động như thế nào phiên bản hiện tại và tương lai của C++ cũng hoạt động.

Với C++ 17 bạn có thể khá nhiều xước std::tie ủng hộ những gì được gọi bindings cấu trúc. Họ cũng làm như vậy (tốt, không giống nhau, nhưng chúng có cùng tác dụng thuần), mặc dù bạn cần nhập ít ký tự hơn, không cần hỗ trợ thư viện và bạn cũng có khả năng tham chiếu, nếu điều đó xảy ra là những gì bạn muốn.

(Lưu ý rằng trong C++ 17 nhà xây dựng làm trích lập luận, vì vậy make_tuple đã trở thành phần nào không cần thiết, quá.)

int a, b; 
std::tie(a, b) = std::make_tuple(2, 3); 

// C++17 
auto [c, d] = std::make_tuple(4, 5); 
auto [e, f] = std::tuple(6, 7); 
std::tuple t(8,9); auto& [g, h] = t; // not possible with std::tie 
+2

Nếu đó dòng cuối cùng biên dịch Tôi là một chút lo lắng. Có vẻ như ràng buộc một tham chiếu đến một tạm thời là bất hợp pháp. –

+0

@NirFriedman Bạn không nhận được gia hạn trọn đời trong trường hợp đó? – Neil

+2

@Neil Nó phải là tham chiếu rvalue hoặc tham chiếu const lvalue. Bạn không thể liên kết tham chiếu lvalue với giá trị prvalue (tạm thời). Mặc dù điều này đã được một "phần mở rộng" trong MSVC cho lứa tuổi. –

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