3

While exploring Policy Based Design pattern in C++, I stumbled upon one problem which I could not find a solution to: How do you write copy and move constructors for policy based classes in a generic way without referring to member variables inside the policy class?

Here's an example:

class Foobase {
public:
  Foobase(int v) :val(v) { }
protected:
  int val;
};

template <typename Base>
class Foo : public Base {
public:
  Foo(int v):Base(v) { }
  // how can I initialize Base() class without referring to it's protected members?
  Foo(const Foo<Base>& other) :Base(other.val) { }
};

int main() {
  Foo<Foobase> foo(5);
  auto foo2 = foo;
}

In the above code, copy constructor of class Foo uses protected member variable to initialize Base class. Is there any other way to initialize Base except above? What are the best practices in such cases?

Updated question:

Answer by @LogicStuff clarifies copy constructor part of the question, but doesn't answer the move constructor question. See the updated example code in which class Foo can also have member variables.

class Foobase {
public:
  Foobase(int v) :val(v) { }
  Foobase(const Foobase&) = default;
  Foobase(Foobase&&) noexcept = default;
  Foobase& operator= (const Foobase&) = default;
  Foobase& operator= (Foobase&&) noexcept = default;
  void Print() const {
    std::cout << val << std::endl;
  }
protected:
  int val;
};

template <typename Base>
class Foo : public Base {
public:
  // works fine
  Foo(std::string str, int v):Base(v), name(str) { }

  // works fine
  Foo(const Foo<Base>& other) :Base(other), name(other.name) { }

  // I'm doubtful about this c'tor, if I move `other` for initializing Base,
  // how can I initialize `name` variable?
  Foo(Foo<Base>&& other)
    :Base(std::move(other)), name(std::move(other.name) /* will this be valid?? */) { }

  // can we have copy and assignment operator for this class?

  void Print() {
    std::cout << "name = " << name << std::endl;
    Base::Print();
  }

private:
  std::string name;
};

int main() {
  Foo<Foobase> foo("foo", 5);
  auto foo2 = std::move(foo);
  foo2.Print();
}
Pranav
  • 560
  • 1
  • 8
  • 21
  • Why are you defining copy constructors in the first place? Use Rule of Zero. – milleniumbug Sep 01 '17 at 19:05
  • copy constructor here is for an example, I'll be following in rule of 5 in real use case scenario! – Pranav Sep 01 '17 at 19:08
  • Not Rule of Five. *Rule of Zero*. In your case, the `Foobase` follows Rule of Zero, and none of the members inside `Foo` need special treatment, so you *don't write any* of the special members for the `Foo` class since the default ones will do all that's needed. – milleniumbug Sep 01 '17 at 20:22
  • Sorry for the confusion, I was simply not defining all the constructors to have smaller example code – Pranav Sep 01 '17 at 20:49

1 Answers1

2

You can simply use Base's copy constructor:

Foo(const Foo<Base>& other) : Base(other) { }

or not define it at all and leave it up to the compiler, if you're not going to do something extra there.

Actually, it can look like this and your example will still compile:

template <typename Base>
class Foo : public Base {
public:
  using Base::Base;
};

After your edit:

Now you have to supplement a constructor taking two parameters (as you did):

template <typename Base>
class Foo : public Base {
public:
  Foo(std::string str, int v) : Base(v), name(str) { }

  void Print() {
    std::cout << "name = " << name << std::endl;
    Base::Print();
  }

private:
  std::string name;
};

but you've also defined the special member functions which would be identically generated by the compiler otherwise (that means they're correct). This is also the case with your original example. Both versions of Foo are move-constructible.

LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • Thanks @LogicStuff for the answer. It does answer my question about copy constructor but probably don't answer move constructor part of it. Perhaps I'm missing anything? Please see the updated question. Thanks! – Pranav Sep 01 '17 at 19:49
  • Please really try to adhere to the rule of 3/5/0. This question is purely based on you not doing that. – LogicStuff Sep 01 '17 at 20:29
  • Sorry for the confusion, I was simply not defining all the constructors to have smaller example code. – Pranav Sep 01 '17 at 20:45
  • Your solution is to not define constructor at all. That's fine, but I wanted to explicitly define or delete the constructors. Your solution does not answer that. – Pranav Sep 01 '17 at 21:46
  • *"that means they're correct"* answers that part. – LogicStuff Sep 01 '17 at 22:08