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();
}
You can simply use
Base
's copy constructor: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:
After your edit:
Now you have to supplement a constructor taking two parameters (as you did):
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.