Question

As I understand copy-and-swap idiom, it has a drawback of needing of bolierplate code. Consider a simple "just-hold-all-those-damn-lemons" struct:

struct MuchData {
private:
    std::string one, two;
    int three;
    std::vector<Something> four;
    MyType five;
    MyOtherType six, seven;
    unsigned long long int still_overflows;

public:
    MuchData() : one("."), two("/"), three(0), four(), five(), six(-1), seven(0), still_overflows(0) 
    { }

    MuchData(const MuchData& rhs) : one(rhs.one), two(rhs.two), three(rhs.three), four(rhs.four), five(rhs.five), six(rhs.six), seven(rhs.seven), still_overflows(rhs.still_overflows) 
    { }

    MuchData(MushData&& old) : one("."), two("/"), three(0), four(), five(), six(-1), seven(0), still_overflows(0)
    { swap(*this, old); }

    MuchData& operator=(MuchData old) { swap(*old, this); return *this; }

    friend void swap(MuchData& left, MushData&right) {
        using std::swap;

        swap(left.one, right.one);
        swap(left.two, right.two);
        swap(left.three, right.three);
        swap(left.four, right.four);
        swap(left.five, right.five);
        swap(left.six, right.six);
        swap(left.seven, right.seven);
        swap(left.still_overflows, right.still_overflows);
    }

    // And now we can go and do something interesting
};

With initializers written in

: one(".")
, two("/")
, three(0)
// etc.

style, this code takes even more space. And MyType and MyOtherType are probably defined with this technique as well... is there any way to reduce the amount of repetitiveness here? When new fields are added, for example, it's extremely easy to forget to add the corresponding swap(...) line, which causes a mysterious slicing.

Was it helpful?

Solution

One thing you could do is store the members in a std::tuple and provide named accessors fot them, like this:

struct MuchData {
private:
  std::tuple<std::string, std::string, int, std::vector<Something>, MyType, MyOtherType, unisgned long long> data;

  std::string& one() { return std::get<0>(data); }
  const std::string& one() const { return std::get<0>(data); }

  //etc.
};

Yes, you're trading one piece of boilerplate for another, but you will only be writing the names once; the constructor syntax will be much more concise, for example. And even if you forget to add an accessor, swap() will work just fine (as it will just swap() the tuples).

With C++1y, you can even use auto& and const auto& for the return type of the accessors, eliminating even more duplication.

OTHER TIPS

You can use in-class initializers to reduce duplication between constructors that initialize fields to their standard values:

class MuchData {
  std::string one{"."};
  std::string two{"/"};
  // ...

Then the default and move constructor become trivial.

MuchData() = default;

MuchData(MuchData&& o) { swap(*this, o); }

And you can default the copy constructor:

  MuchData(const MuchData&) = default;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top