2013-03-13 3 views
3

Я прочитал Dave Abrahams article на RVO и еще несколько Q/As на SO (14043609, 9293726 и 10818278), но у меня все еще есть вопрос. Когда я компилирую и запускаю следующий код, я получаю этот вывод:Почему RVO не происходит здесь?

Address of v in func 0x7fffac6df620 
Address of v.data in func  0x2081010 
Address of v in main 0x7fffac6df690 
Address of v.data in func  0x20811b0 
9 

Мне кажется, что копия сделана. Как передать большие объекты из функций? Обратите внимание, что я хочу вернуть один или несколько объектов, не создавая для него явную структуру. Я использовал GCC 4.6.3 с -O2. Изменить: первые два ответа показали мне, что я слишком много ожидал от компилятора. Я добавил main2, который ведет себя одинаково, например. печатные адреса различны. Я хотел бы подчеркнуть, что мотивация - это эффективное возвращение больших объектов.

#include <iostream> 
#include <vector> 
#include <tuple> 

std::tuple<std::vector<int>, double> func() { 
    std::vector<int> v; 
    v.reserve(100); 
    for (int k=0;k!=100;k+=1) 
    v.push_back(k); 

    double a = 5.0; 
    std::cout << "Address of v in func\t" << &v << std::endl; 
    std::cout << "Address of v.data in func\t" << v.data() << std::endl; 
    return make_tuple(v, a); 
} 

int main() { 
    std::vector<int> v; 
    double a; 
    std::tie(v, a) = func(); 
    std::cout << "Address of v in main\t" << &v << std::endl; 
    std::cout << "Address of v.data in func\t" << v.data() << std::endl; 
    std::cout << v[9] << std::endl; 
    return 0; 
} 


int main2() { 
    auto tp = func(); 
    std::vector<int> & v = std::get<0>(tp); 
    double & a = std::get<1>(tp); 
    std::cout << "Address of v in main\t" << &v << std::endl; 
    std::cout << "Address of v.data in func\t" << v.data() << std::endl; 
    std::cout << v[9] << std::endl; 
    return 0; 
} 

ответ

4

Как уже говорилось, есть две вещи, которые предотвращают RVO. Функция не возвращает v, а вместо этого кортеж, который сконструирован в форме v и a. Также в основной функции v присваивается и не строится из возвращаемого значения.

Чтобы получить то, что вы хотите, вы можете использовать кортежи напрямую без дополнительных векторных объектов:

#include <iostream> 
#include <vector> 
#include <tuple> 

std::tuple<std::vector<int>, double> func() { 
    std::tuple<std::vector<int>, double> t; 
    get<0>(t).reserve(100); 
    for (int k=0;k!=100;k+=1) 
    get<0>(t).push_back(k); 

    get<1>(t) = 5.0; 
    std::cout << "Address of v in func\t" << &get<0>(t) << std::endl; 
    std::cout << "Address of v.data in func\t" << get<0>(t).data() << std::endl; 
    return t; 
} 

int main() 
{ 
    std::tuple<std::vector<int>, double> t = func(); 
    std::cout << "Address of v in main\t" << &get<0>(t) << std::endl; 
    std::cout << "Address of v.data in func\t" << get<0>(t).data() << std::endl; 
    std::cout << get<0>(t)[9] << std::endl; 

    return 0; 
} 

Выход:

Address of v in func 0x28fe80 
Address of v.data in func  0x962c08 
Address of v in main 0x28fe80 
Address of v.data in func  0x962c08 
9 

Альтернативной оптимизацией является использование перемещения семантики при построении кортежа:

return make_tuple(std::move(v), a); 

В этом случае, по крайней мере, копирование внутреннего вектора вектора b uffer избежать:

Address of v in func 0x28fdd4 
Address of v.data in func  0xa72c08 
Address of v in main 0x28fe64 
Address of v.data in func  0xa72c08 
9 
3

Так как v и a были объявлены как переменные в main(), не копия игнорировать. То, что вы получаете здесь, - это копирование, а не копирование. Это эквивалент этого:

struct Foo {}; 

Foo foo() { return Foo(); } 

int main() 
{ 
    Foo f1; 
    f1 = foo(); // no copy hence f1 is distinct from object returned 
    Foo f2 = foo(); // We can get RVO here, returned object can be f2. 
} 
+0

Я добавил второй пример, который должен быть эквивалентен вашему коду. Однако он все еще не использует RVO. Я что-то упустил? – Mankka

2

РВО может быть очень хорошо здесь происходит, но это единственная возможность для копирования элизии в коде, который вы дали копируете возвращаемое значение make_tuple(v, a) в возвращаемом значение func().

Независимо от того, сделано это или нет, std::vector и double все равно будут скопированы. Вы просто присваиваете результат от func() до v и a в main. Копирование elision (и RVO) применяется только к копированию/перемещению конструкции, а не к назначению.

Когда вы &v в main, вы просто получаете адрес v объекта, определенного в первой строке main. Конечно, это отличается от объекта v, определенного в func.

2

В первом примере данные копируются в назначении:

int main() { 
    std::vector<int> v; 
    double a; 
    std::tie(v, a) = func(); 

В вашем втором примере, данные по-прежнему копируется, когда вы делаете кортеж.Этот модифицированный пример показывает, что RVO действительно происходит:

#include <iostream> 
#include <vector> 
#include <tuple> 

std::tuple<std::vector<int>, double> func() { 
    std::vector<int> v; 
    v.reserve(100); 
    for (int k=0;k!=100;k+=1) 
    v.push_back(k); 

    double a = 5.0; 

    const auto ret = make_tuple(v, a); 
    const auto &v1 = std::get<0>(ret); 

    std::cout << "Address of v in func\t" << &v1 << std::endl; 
    std::cout << "Address of v.data in func\t" << v1.data() << std::endl; 

    return ret; 
} 

int main() { 
    auto tp = func(); 
    std::vector<int> & v = std::get<0>(tp); 
    double & a = std::get<1>(tp); 
    std::cout << "Address of v in main\t" << &v << std::endl; 
    std::cout << "Address of v.data in func\t" << v.data() << std::endl; 
    std::cout << v[9] << std::endl; 

    (void)a; 
} 
0

Благодарим за ответы. Я нашел Timo's answer наиболее полезным. Вот как я адаптировал этот ответ к моему собственному стилю. Обратите внимание на дублированный шаблон в обоих случаях: func и main. Конечно, если кто-то знает, как избавиться от этого, было бы здорово!

#include <iostream> 
#include <vector> 
#include <tuple> 

std::tuple<std::vector<int>, double> func() { 
    std::tuple<std::vector<int>, double> tp; 
    std::vector<int> & v = std::get<0>(tp); 
    double & a = std::get<1>(tp); 

    v.reserve(100); 
    for (int k=0;k!=100;k+=1) 
    v.push_back(k); 

    a = 5.0; 
    std::cout << "Address of v in func\t" << &v << std::endl; 
    std::cout << "Address of v.data in func\t" << v.data() << std::endl; 
    return tp; 
} 

int main() { 
    std::tuple<std::vector<int>, double> tp = func(); 
    std::vector<int> & v = std::get<0>(tp); 
    double & a = std::get<1>(tp); 

    std::cout << "Address of v in main\t" << &v << std::endl; 
    std::cout << "Address of v.data in func\t" << v.data() << std::endl; 
    std::cout << v[9] << std::endl; 

    (void)a; 
    return 0; 
} 
Смежные вопросы