Ниже приведен пример моего типичного кода. Есть много объектов, которые выглядят так:
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)) {}
,У меня сильное чувство, что по эмпирическим правилам они несовершенны и просто неверны.
После прочтения cppreference, Скотта Майерса, стандарта C ++, Страуструпа и т. Д. Я чувствую: «Да, я понимаю каждое слово здесь, но оно по-прежнему не имеет никакого смысла». Единственное, что я царь понял, — это семантика перемещения имеет смысл, когда мой класс содержит не копируемые типы, такие как std::mutex
а также std::unique_ptr
,
Я видел много кода, в котором люди передают сложный объект по значению, например, большие строки, векторы и пользовательские классы — я считаю, что именно здесь происходит семантика перемещения, но, опять же, как вы можете передать объект функции путем перемещения? Если я прав, он оставил бы объект в «своего рода нулевом состоянии», что сделало бы его непригодным для использования.
Итак, вопросы:
Правильно выбрать между передачей по значению и передачей по ссылке?
— Нужно ли предоставлять как копировать, так и перемещать конструкторы?
— Нужно ли явно писать перемещение и копировать конструкторы? Могу ли я использовать = default
? Мои классы в основном являются объектами POD, поэтому здесь нет сложных входов в систему.
— при отладке всегда могу написать std::cout << "move\n";
или же std::cout << "copy\n";
в конструкторах моих собственных классов, но как мне узнать, что происходит с классами из stdlib
?
Постскриптум Может показаться, что это крик отчаяния (это так), а не действительный вопрос SO. Я просто не знаю, чтобы сформулировать свои проблемы лучше, чем это.
Если это примитивный тип, передайте по значению. Местность ссылки выигрывает.
Если вы не собираетесь хранить его копию, передайте по значению или 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()
настроить правильно. Но это то, что выходит за рамки этого вопроса.
Других решений пока нет …