2014-02-18 3 views
3

Я попытался следующий код:вектор :: push_back и станд :: двигаться

#include <iostream> 

struct test{ 
    test(){} 
    test(test const &){ 
     std::cout<<__LINE__<<"\n"; 
    } 
    test(test&&){ 
     std::cout<<__LINE__<<"\n"; 
    } 
}; 

#include <vector> 
#include <utility> // std::move 
int main(){ 
    auto&& tmp = test(); 
    std::vector<test> v; 
    v.push_back(tmp); 
    std::cout<<__LINE__<<"\n"; 
    v.push_back(std::move(tmp)); 

    return 0; 
} 

Выход vs2013 Компилятор:

6 // скопировать

9 // переезд

9 // переезд

Г ++ и лязг ++ выход:

6 // скопировать

9 // двигаться

6 // скопировать

Мои проблемы:

  1. Является типом теста tmp & &? Является ли tmp значением r?

  2. Если тип tmp является тестом & &, почему первый конструктор перемещения push_back не использовал?

  3. Откуда взялся последний выход? Почему vs2013 и g ++ выдают разные результаты?

Спасибо.

Ответ на 3-й вопрос: Он исходит из перераспределения, как прокомментировал andrew.punnett.

+0

Я изменен свой ответ, чтобы расширить комментарий Эндрю. – Potatoswatter

+0

Наиболее эффективным решением является 'v.emplace_back();' который не требует ни копий, ни ходов. – fredoverflow

+0

Примечание: 'v.emplace_back()' перемещает объект, если он был создан до вызова – DarkWanderer

ответ

4

Является ли тип tmptest&&?

Да и нет. tmp является ссылочной переменной rvalue типа test&&, но идентификатор tmp, взятый в качестве выражения, имеет тип test и категория стоимости lvalue. & никогда не является частью типа выражения.

tmp a rvalue?

No. Любое использование идентификатора - это выражение lvalue, даже имя ссылки rvalue. Контрольные переменные Rvalue доступны по существу идентично ссылочным переменным lvalue; только decltype(tmp) можно сказать разница. (Часто вы будете использовать decltype((tmp)) для избежать говорить разницу.)

Если тип ТМР тест & &, почему не сделал первый использование push_back двигаться конструктор?

Поскольку имя ссылки rvalue по-прежнему является значением lvalue. Чтобы получить выражение rvalue, используйте move(tmp).

Откуда взялся последний выход? Почему vs2013 и g ++ выдают разные результаты?

Clang и GCC только поместили помещение для одного объекта в vector по умолчанию. Когда вы добавили второй объект, хранилище векторов было перераспределено, в результате чего объекты были скопированы. Почему они не двигались? Поскольку конструктор перемещения не является noexcept, поэтому, если он выдал исключение, было бы невозможно отменить перераспределение.

Что касается двух перемещений по MSVC, есть две возможности, которые вы можете отличить по экспериментам - у меня нет копии.

  1. MSVC зарезервировано достаточно места внутри вектора для двух объектов по умолчанию. Второй шаг - от внутренней локальной переменной.
  2. MSVC проигнорировал требование, чтобы конструктор перемещения был noexcept и вызвал его для выполнения перемещения в любом случае. Это было бы ошибкой, но в этом случае она покрывает распространенную ошибку.

Если заменить vectordeque с, вы не увидите больше копий, потому что deque не позволили предположить copyability.

+3

В основном исправлены, но дополнительные вызовы конструктора состоят только в том, что вектор перемещается без использования «временной внутренней переменной». Например, попробуйте v.reserve (2) сразу после создания вектора. –

+0

Не существует ли здесь какой-то ссылочной коллапсирующей хитрости, т. Е. 'Tmp' - это то, что возвращается RHS? Это то же самое, что и все «универсальные ссылки». – juanchopanza

+0

Так что test && не имеет превосходства против теста &? – cqdjyy01234

2

Visual Studio еще не поддерживает ключевое слово noexcept и, вероятно, не соответствует безопасности исключений push_back. Кроме того, дополнительный выход является результатом разницы в вычислении производительности при росте.

#include <iostream> 
#include <vector> 
#include <utility> // std::move 

struct Except{ 
    Except(){} 
    Except(Except const &) { 
     std::cout<< "COPY\n"; 
    } 
    Except(Except&&) { 
     std::cout<< "MOVE\n"; 
    } 
}; 

struct NoExcept{ 
    NoExcept(){} 
    NoExcept(NoExcept const &) noexcept { 
     std::cout<< "COPY\n"; 
    } 
    NoExcept(NoExcept&&) noexcept { 
     std::cout<< "MOVE\n"; 
    } 
}; 

template <typename T> void Test(char const *title,int reserve = 0) { 
    auto&& tmp = T(); 
    std::cout<< title <<"\n"; 
    std::vector<T> v; 
    v.reserve(reserve); 

    std::cout<< "LVALUE REF "; 
    v.push_back(tmp); 
    std::cout<< "RVALUE REF "; 
    v.push_back(std::move(tmp)); 
    std::cout<< "---\n\n"; 
} 
int main(){ 
    Test<Except>("Except class without reserve"); 
    Test<Except>("Except class with reserve", 10); 
    Test<NoExcept>("NoExcept class without reserve"); 
    Test<NoExcept>("NoExcept class with reserve", 10); 
} 

И результат в звоне:

Except class without reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
COPY 
--- 

Except class with reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
--- 

NoExcept class without reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
MOVE 
--- 

NoExcept class with reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
--- 
Смежные вопросы