Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > C++11 move семантика


Автор: wladF 3.3.2013, 07:57
Всем привет. 
В Интернете можно найти множество различных примеров использования данного нововведения в C++11, но практически все они сводятся к такому коду:
Код

class Container 
{
    Container(const Container& container) { /* обычный копирующий конструктор */ }

    Container(Container&& container) 
    {
        m_pData = container.m_pData;
        m_size = container.m_size;
    
        container.m_pData = nullptr;
        container.m_size = 0;
    }
    //..
};

int main() 
{
    Container a = {/*...*/};
    Container b(a); // медленно
    Container c(std::move(b)); // быстро, при этом b становится пустым    
}


Зачем нужна была здесь семантика переноса, если, грубо говоря, кроме как другого имени переменной мы не получили? 

Т.е. мы могли бы сделать, что-то типа:
Код

Container a = {/*...*/};
Container& c = a; // ну не нравится нам имя "а" ! теперь будет "c", и никакого копирования

Либо просто дальше использовать "a", не создавая других переменных.

Автор: NoviceF 3.3.2013, 09:15
Безотносительно данного примера, move семантика, например, даёт возможность использования умных указателей без подсчёта ссылок и с семантикой передачи владения (а-ля auto_prt) в контейнерах STL, пример - unique_ptr (с ограничениями на копирование).

http://stackoverflow.com/questions/2876641/so-can-unique-ptr-be-used-safely-in-stl-collections

Автор: JackYF 3.3.2013, 14:55
Цитата(wladF @  3.3.2013,  06:57 Найти цитируемый пост)
но практически все они сводятся к такому коду

Этот пример сам по себе не очень хорош, но его можно легко дополнить:

Код

std::vector< Container > xyz;
xyz.push_back(a); // медленно
xyz.push_back(std::move(a)); // быстро

Автор: xvr 4.3.2013, 13:54
Мое IMHO - это было сделано для ускорения контейнелов, которые могут перемещать хранимые объекты в процессе своей жизни (std::vector например). Если vector делает resize, то без move семнтики его элементов, ему придется для каждого своего элемента позвать copy конструктор (для нового елемента), а потом деструктор (для старого), и то и другое может быть весьма дорого. При наличии move семантики будет вызван move constructor (что должно быть дешево) и деструктор для пустого элемента (что так же должно быть дешево)

Автор: volatile 5.3.2013, 00:00
Из FAQ'а Страуса. (вольный перевод)

Старый своп.
Код

template<class T> 
swap(T& a, T& b)  // "old style swap"
{
   T tmp(a);   // now we have two copies of a
   a = b;      // now we have two copies of b
   b = tmp;    // now we have two copies of tmp (aka a)
}

Теперь представим что мы вызываем его для двух строк, размером по мегабайту.
string str1 = "Очень длинная строка1 .....";
string str2 = "Очень длинная строка2 .....";
swap (str1, str2);
Здесь нам понадобицца дополнительная память в 1 мегабайт, плюс время для копирования 3 мегабайт.

Идеальный своп
Код

template<class T>
void swap(T& a, T& b)  // "perfect swap" (almost)
{
   T tmp = move(a);    // could invalidate a
   a = move(b);        // could invalidate b
   b = move(tmp);      // could invalidate tmp
}
Здесь произойдет мгновенный обмен, без дополнительных затрат памяти

Автор: Acer 8.3.2013, 14:32
Цитата(JackYF @ 3.3.2013,  14:55)
Цитата(wladF @  3.3.2013,  06:57 Найти цитируемый пост)
но практически все они сводятся к такому коду

Этот пример сам по себе не очень хорош, но его можно легко дополнить:

Код

std::vector< Container > xyz;
xyz.push_back(a); // медленно
xyz.push_back(std::move(a)); // быстро

Для улучшения скорости этот пример бессмыслен, так как push_back тормозит, даже если сделать reserve()
Лучше уж так:
Код

std::vector<Container> xyz;
xyz.resize(num_items);
for (size_t i = 0; i < num_items; ++i)
    xyz[i] = std::move(a[i]);


Ps

Код

std::vector<Container> xyz;
xyz.resize(1);
xyz[0] = std::move(a);

Автор: feodorv 8.3.2013, 14:56
Цитата(Acer @  8.3.2013,  15:32 Найти цитируемый пост)
for (size_t i = 0; i < num_items; ++i)
    xyz[i] = std::move(a);

Несколько раз подряд делать move для одной и той же переменной - это разве правильно?

Автор: Acer 8.3.2013, 15:30
Цитата(feodorv @ 8.3.2013,  14:56)
Цитата(Acer @  8.3.2013,  15:32 Найти цитируемый пост)
for (size_t i = 0; i < num_items; ++i)
    xyz[i] = std::move(a);

Несколько раз подряд делать move для одной и той же переменной - это разве правильно?

Нет. Ошибся. Пусть будет там a[i], например

Автор: volatile 8.3.2013, 23:23
Цитата(Acer @  8.3.2013,  14:32 Найти цитируемый пост)
Для улучшения скорости этот пример бессмыслен, так как push_back тормозит, даже если сделать reserve()
Лучше уж так:
std::vector<Container> xyz;
xyz.resize(num_items);
for (size_t i = 0; i < num_items; ++i)
    xyz[i] = std::move(a[i]);


При resize будут вызваны дефолтные конструкторы для добавляемых объектов.
(абсолютно лишняя операция, потому-что их сразу следом затрут)
При пуш-бэке дефолтные конструкторы вызваны не будут, а сразу объект будет перемещен на нужное место.
Сам пуш-бэк, при предварительном резерве, приктически ничо не стоит. 


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)