2016-12-01 4 views
4

This code compiles но мне интересно, какая версия должна быть предпочтительнее:станд :: галстук против станд :: make_tuple

#include <iostream> 
#include <tuple> 
using namespace std; 

tuple<int, int, int> return_tuple1() { 
    int a = 33; 
    int b = 22; 
    int c = 31; 
    return tie(a, b, c); 
} 

tuple<int, int, int> return_tuple2() { 
    int a = 33; 
    int b = 22; 
    int c = 31; 
    return make_tuple(a, b, c); 
} 

int main() { 
    auto a = return_tuple1(); 
    auto b = return_tuple2(); 
    return 0; 
} 

поскольку функция возвращает кортеж по значению не должно быть никаких проблем в использовании std::tie правильно? (т. е. никаких оборванных ссылок)

ответ

1

Будьте очень осторожны с std::tie. Возврат tie логически эквивалентен возврату ссылки со всеми предостережениями, которые прилагаются к ней.

Логически, эти три эквивалентны:

int& foo(); 
std::reference_wrapper<int> foo(); 
std::tuple<int&> foo(); 

и это:

int a = 10; 
return std::tie(a); 

эквивалентно следующему:

int a = 10; 
return std::ref(a); 

, поскольку он производит один из них:

std::tuple<int&> 

В вашем примере вы сохраняете неявное преобразование возвращаемого значения. Однако, заменив тип возвращаемого с auto показывает логическую ошибку:

#include <iostream> 
#include <tuple> 
using namespace std; 

auto return_tuple1() { // function name is now lying 
    int a = 33;   // it should be return_chaos() 
    int b = 22; 
    int c = 31; 
    return tie(a, b, c); 
} 

auto return_tuple2() { 
    int a = 33; 
    int b = 22; 
    int c = 31; 
    return make_tuple(a, b, c); 
} 

int main() { 
    auto a = return_tuple1(); // uh-oh... 

    auto b = return_tuple2(); 

    std::get<0>(a); // undefined behaviour - if you're lucky you'll get a segfault at some point. 
    std::get<0>(b); // perfectly ok 
    return 0; 
} 
8

std::tie не будет делать то, что вы думаете.
std::tie возвращает tuple из ссылки к элементам прошло, так что в return_tuple1(), что на самом деле происходит, является:

tuple<int, int, int> return_tuple1() { 
    int a = 33; 
    int b = 22; 
    int c = 31; 
    return std::tuple<int&,int&,int&>(a,b,c); 
} 

затем, возвращаемый тип tuple<int, int, int>строит себе от std::tuple<int&,int&,int&>.

сейчас, компилятор может оптимизировать эту конструкцию, но я бы не стал спорить об этом. используйте std::make_tuple, так как это правильный инструмент для этой задачи.

+0

Также 'станд :: make_tuple' лучше выражает намерение кода. Если бы я увидел этот код, вызывающий 'std :: tie', а затем сохраняя его в' tuple', я бы подумал, что это ошибка. – SirGuy

1

tuple2 должен быть более эффективным. tie действительно создает tuple, но вещь, которую вы должны помнить о tie, заключается в том, что она не делает кортеж типа Args..., но вместо этого делает кортеж типа Args&..., что означает, что ваш тип возвращаемого значения и ваш кортеж не совпадают. Это означает, что вам нужно скопировать из привязанного кортежа в верный кортеж.

Во втором примере вы возвращаете кортеж с тем же типом, что и тип возврата, чтобы вы могли вернуть это напрямую. Вам не нужно копировать.

Смежные вопросы