перемещать или копировать при передаче аргументов в конструктор и функции-члены

Ниже приведен пример моего типичного кода. Есть много объектов, которые выглядят так:

struct Config
{
Config();
Config(const std::string& cType, const std::string& nType); //additional variables omitted
Config(Config&&) = default;
Config& operator=(Config&&) = default;

bool operator==(const Config& c) const;
bool operator!=(const Config& c) const;

void doSomething(const std::string& str);
bool doAnotherThing(const MyOtherObject& obj);
void doYetAnotherThing(int value1, unsigned long value2, const std::string& value3, MyEnums::Seasons value4, const std::vector<MySecondObject>& value5);

std::string m_controllerType;
std::string m_networkType;
//...
};

//...

Config::Config(const std::string& cType, const std::string& nType) :
m_controllerType(cType),
m_networkType(nType)
{
}

Мои мотивы и общее понимание предмета:

  • используйте константные ссылки в конструкторах и методах, чтобы избежать двойного копирования при передаче объектов.
  • простые типы — передача по значению; классы и структуры — проходите по константной ссылке (или простой ссылке, когда мне нужно изменить их)
  • заставить компилятор создать default переместите конструктор и переместите задание так, чтобы он мог выполнять свою причудливую магию и одновременно позволять избежать скучного письма ctor() : m_v1(std::move(v1)), m_v2(std::move(v2)), m_v3(std::move(v3)) {},
  • если он работает плохо, используйте указатели libc и raw, затем оберните его в классе и напишите комментарий.

У меня сильное чувство, что по эмпирическим правилам они несовершенны и просто неверны.

После прочтения cppreference, Скотта Майерса, стандарта C ++, Страуструпа и т. Д. Я чувствую: «Да, я понимаю каждое слово здесь, но оно по-прежнему не имеет никакого смысла». Единственное, что я царь понял, — это семантика перемещения имеет смысл, когда мой класс содержит не копируемые типы, такие как std::mutex а также std::unique_ptr,

Я видел много кода, в котором люди передают сложный объект по значению, например, большие строки, векторы и пользовательские классы — я считаю, что именно здесь происходит семантика перемещения, но, опять же, как вы можете передать объект функции путем перемещения? Если я прав, он оставил бы объект в «своего рода нулевом состоянии», что сделало бы его непригодным для использования.

Итак, вопросы:
Правильно выбрать между передачей по значению и передачей по ссылке?
— Нужно ли предоставлять как копировать, так и перемещать конструкторы?
— Нужно ли явно писать перемещение и копировать конструкторы? Могу ли я использовать = default? Мои классы в основном являются объектами POD, поэтому здесь нет сложных входов в систему.
— при отладке всегда могу написать std::cout << "move\n"; или же std::cout << "copy\n"; в конструкторах моих собственных классов, но как мне узнать, что происходит с классами из stdlib?

Постскриптум Может показаться, что это крик отчаяния (это так), а не действительный вопрос SO. Я просто не знаю, чтобы сформулировать свои проблемы лучше, чем это.

0

Решение

  • Если это примитивный тип, передайте по значению. Местность ссылки выигрывает.

  • Если вы не собираетесь хранить его копию, передайте по значению или const&,

  • Если вы хотите хранить ее копию, и ее очень дешево переместить и скромно дорого скопировать, пройдите мимо значение.

  • Если что-то требует умеренных затрат на перемещение и является параметром приемника, рассмотрите возможность передачи по ссылке rvalue. Пользователи будут вынуждены std::move,

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

Правило от 0/3/5 описывает, как вы должны обрабатывать копии назначить / построить / уничтожить. В идеале вы следуете правилу 0; копировать / перемещать / уничтожать это все =default во всем, кроме типов управления ресурсами. Если вы хотите реализовать любой копирования / перемещения / уничтожения, вам нужно реализовать, =default или же =delete каждый второй из 5.

Если вы принимаете только один аргумент для установщика, попробуйте написать && а также const& версии сеттера. Или просто разоблачение основного объекта. Перемещение-назначение иногда повторно использует хранилище, и это эффективно.

Выделение выглядит так:

struct emplace_tag {};
struct wrap_foo {
template<class...Ts>
wrap_foo(emplace_tag, Ts&&...ts):
foo( std::forward<Ts>(ts)... )
{}
template<class T0, class...Ts>
wrap_foo(emplace_tag, std::initializer_list<T0> il, Ts&&...ts):
foo( il, std::forward<Ts>(ts)... )
{}
private:
Foo foo;
};

Есть множество других способов, которыми вы можете разрешить конструкцию «emplace». Увидеть emplace_back или же emplace в стандартных контейнерах (где они используют размещение ::new для создания объектов, пересылка объектов, переданных в).

Конструкция Emplace позволяет даже прямое строительство, даже не двигаясь, используя объекты с operator T() настроить правильно. Но это то, что выходит за рамки этого вопроса.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]