Передача std :: vector в конструктор и перемещение семантики - PullRequest
11 голосов
/ 01 апреля 2012

В выходные дни я пытаюсь освежить свои навыки c ++ и выучить немного c ++ 11, я наткнулся на следующую проблему: я не могу заставить мой контейнерный класс правильно использовать конструктор перемещения:

У меня есть класс построителя, который определяется следующим образом:

class builder
{
   ...
   container build() const
   {
     std::vector<items> items;

     //... fill up the vector

     return container(items); //should move the vector right? wrong!
     //return container(std::move(items)); also doesn't work
   }
}

И классы item и container определены следующим образом:

class container
{
public:

    container(std:vector<item> items)
      : items_(items) // always invokes copy constructor on vector, never move
    { }

    container(container&& rhs)
    {
       ...
    }

    ...

private:
    std::vector<item> items_;

}

class item
{
public:
    //move .ctor
    item(item && rhs);
    item& operator=(item && rhs);

    //copy .ctor
    item(const item& rhs); //this gets called instead of move .ctor
    item& operator=(const item& rhs);

    ...
}

Теперь мой код просто использует

builder my_builder;
...
auto result = my_builder.build();

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

Как мне написать следующую классификацию, чтобы не копировать элементы?Должен ли я просто вернуться к использованию стандартных указателей?

Ответы [ 2 ]

22 голосов
/ 01 апреля 2012

Ваш код должен быть изменен на:

container(std:vector<item2> items) // may copy OR move
: items_(std::move(items)) // always moves
{}

В общем: если вы хотите иметь свою собственную копию чего-либо, сделайте эту копию в этом списке параметров и переместите ее туда, где она должна быть.Пусть звонящий будет тем, кто решит, будут ли они копировать или перемещать существующие данные.(Другими словами, вы были на полпути. Теперь просто переместите ваши данные.)

Также: return container(std::move(items));.Я не упоминал об этом раньше, потому что я ошибочно думал, что все локальные переменные были автоматически перемещены в операторе возврата, но только возвращаемое значение.(Так что на самом деле это должно работать: return items;, потому что конструктор container не explicit.)

5 голосов
/ 17 октября 2012

Написал для вас этот шаблон с поддержкой перемещения. Изучите его, и вы получите его.

/// <summary>Container.</summary>
class Container {
private:
    // Here be data!
    std::vector<unsigned char>  _Bytes;

public:
    /// <summary>Default constructor.</summary>
    Container(){
    }

    /// <summary>Copy constructor.</summary>
    Container(const Container& Copy){
        *this = Copy;
    }

    /// <summary>Copy assignment</summary>
    Container& operator = (const Container& Copy){
        // Avoid self assignment
        if(&Copy == this){
            return *this;
        }
        // Get copying
        _Bytes = Copy._Bytes; // Copies _Bytes
        return *this;
    }

    /// <summary>Move constructor</summary>
    Container(Container&& Move){
        // You must do this to pass to move assignment
        *this = std::move(Move); // <- Important
    }

    /// <summary>Move assignment</summary>
    Container& operator = (Container&& Move){
        // Avoid self assignment
        if(&Move == this){
            return *this;
        }
        // Get moving
        std::swap(_Bytes, Move._Bytes); // Moves _Bytes
        return *this;
    }
}; // class Container

Я всегда против использования аргументов значения, подобных этому:

function(std:vector<item2> items)

Я всегда использую либо:

function(const std:vector<item2>& items)
function(std:vector<item2>& items)
function(std:vector<item2>&& items)

, особенно для больших контейнеров данных, и редко:

function(std:vector<item2> items)

для небольших данных, но не векторов.

Таким образом, вы контролируете, что происходит, и поэтому мы делаем C ++, чтобы контролировать все.

  • Если вам нужна копируемая копия, просто скопируйте ссылку на const самостоятельно в переменную.
  • Если вам нужна только для чтения, ссылка на const предотвращает создание новой копии.
  • Если вы хотите редактировать оригинал, просто используйте ссылку.
  • И используйте аргументы значения длянебольшие данные, когда вам лень.

Очевидно, все зависит от того, что вы делаете.

Я - самоучка, разработчик C ++.Далеко не эксперт, особенно в сленге C ++ ... но учится :) 1032 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...