Let's say I have a container object that stores an std::vector
of polymorphic children.
struct Child
{
Child(Parent& mParent) { /* ... */ }
virtual ~Child() { }
};
class Parent
{
private:
std::vector<std::unique_ptr<Child>> children;
template<typename T, typename... TArgs>
auto& mkChild(TArgs&&... mArgs)
{
// `static_assert` that `T` is derived from `Child`...
children.emplace_back(std::make_unique<T>(std::forward<TArgs>(mArgs)...));
return *children.back();
}
public:
template<typename T, typename... TArgs>
auto& add(TArgs&&... mArgs)
{
mkChild<T>(std::forward<TArgs>(mArgs)...));
return *this;
}
};
Now I can use the Parent
class like this:
int main()
{
Parent p;
p.add<Child1>(some_args1).add<Child2>(some_args2);
}
While this syntax achieves what I want to do (chaining addition of children to a single parent), I find it very hard to read, especially in my real use case.
I would really like to use operator<<
instead. But I cannot figure out a way to construct the children in place.
// Desired syntax
int main()
{
Parent p;
p << mk<Child1>(some_args1) << mk<Child2>(some_args2);
}
Notice how I never specify the parent in the mk
function.
I do not want to say mk<Child1>(p, some_args1)
. The compiler should figure out p
from the chaining of operator<<
.
Is there any way I can implement this mk
function generating code equal to the one generated via the .add<T>(...)
chaining?
The only way I managed to implement this is using a man-in-the-middle struct that holds the construction variadic parameters for the child class.
template<typename T, typename... TArgs> struct DeferCtor
{
std::tuple<TArgs...> ctorArgs;
};
Then operator<<(DeferCtor<T, TArgs...>&)
would deal with the object's construction inside Parent
.
Is there a way to avoid this step, while still having the desired syntax? (Not passing the parent instance in the mk
function.)
You aren't really making objects in place in your existing code -- you're making the child objects on the heap with a unique_ptr
and then moving that unique_ptr
into parent. You can do the same with your operator<<
if you just define it as taking a unique_ptr
:
Parent &Parent::operator<<(std::unique_ptr<Child> ch) {
children.emplace_back(std::move(ch)); }
Now assuming your mk
global function is essentially just an alias for make_unique
:
template<typename T, typename... TArgs>
std::unique_ptr<T> mk(TArgs&&... mArgs) {
return std::make_unique<T>(std::forward<TArgs>(mArgs)...)); }
you should be able to use your desired syntax.
(based on my previous comments). I suggest you construct the unique_ptr of base and feed that to opertor<<
(ideone link). No need to get fancy or complicated.
using namespace std;
using mk = make_unique;
#include <memory>
#include <iostream>
class B {};
class D : public B {};
class E : public B {};
class A {
public:
A & operator << ( std::unique_ptr<B> bp){
std::cout << " added a value " << std::endl;
// children.push_back(move(bp));
return *this;
}
};
int main() {
// your code goes here
A a;
a << mk<D>( .. some argument ) << mk<E>( other arguments) ;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With