mercredi 27 juin 2018

move or copy when passing arguments to the constructor and member functions

The following is an example of my typical code. A have a lot of objects that look like this:

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)
{
}

My motivations and general understand of the subject:

  • use const references in constructors and methods to avoid double-copying when passing objects.
  • simple types - pass by value; classes and structs - pass by const reference (or simple reference when I need to modify them)
  • force compiler to create default move constructor and move assignment so that It would be able to do it's fancy magic and simultaneously it allows to avoid writing boring ctor() : m_v1(std::move(v1)), m_v2(std::move(v2)), m_v3(std::move(v3)) {}.
  • if it performs badly, use libc and raw pointers, then wrap it at class and write a comment.

I have a strong feeling that by rules of thumb are flawed and simply incorrect.

After reading cppreference, Scott Mayers, C++ standard, Stroustrup and so on, I feel like: "Yea, I understand every word here, but it still doesn't make any sense'. The only thing I king of understood is that move semantics makes sense when my class contains non-copiable types, like std::mutex and std::unique_ptr.

I've seen a lot of code where people pass complex object by value, like large strings, vectors and custom classes - I believe this is where move semantics happen, but, again, how can you pass an object to a function by move? If I am correct, it would leave an object in a "kind-of-null-state", making it unusable.

So, the questionы are: - How do I correctly decide between pass-by-value and pass-by-reference? - Do I need to provide both copy and move constructors? - Do I need to explicitly write move and copy constructors? May I use = default? My classes are mostly POD object so there is no complex login involved. - When debugging, I can always write std::cout << "move\n"; or std::cout << "copy\n"; in constructors of my own classes, but how do I know what happens with classes from stdlib?

P.S. It may look like it is a cry out of desperation (it is), not a valid SO question. I simply don't know to formulate my problems better than this.

Aucun commentaire:

Enregistrer un commentaire