避免使用 `operator<<` 延迟 "child" object 构造
Avoiding to defer "child" object construction with `operator<<`
假设我有一个容器 object 存储 std::vector
的多态 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;
}
};
现在我可以像这样使用 Parent
class:
int main()
{
Parent p;
p.add<Child1>(some_args1).add<Child2>(some_args2);
}
虽然这个语法实现了我想做的事情(将 children 的加法链接到单个 parent),但我发现它很难阅读,尤其是在我的实际用例中。
我真的很想改用 operator<<
。但是我想不出一种方法来构建children。
// Desired syntax
int main()
{
Parent p;
p << mk<Child1>(some_args1) << mk<Child2>(some_args2);
}
请注意我从未在 mk
函数中指定 parent。
不想说mk<Child1>(p, some_args1)
。编译器应该从 operator<<
.
的链接中找出 p
有什么方法可以实现这个 mk
函数生成的代码等于通过 .add<T>(...)
链接生成的代码吗?
我设法实现它的唯一方法是使用 man-in-the-middle 结构,该结构包含 child class 的构造可变参数.
template<typename T, typename... TArgs> struct DeferCtor
{
std::tuple<TArgs...> ctorArgs;
};
然后operator<<(DeferCtor<T, TArgs...>&)
会处理object在Parent
中的构造。
有没有办法避免这一步,同时仍然具有所需的语法? (不在 mk
函数中传递 parent 实例。)
您并没有真正在现有代码中创建 objects —— 您正在使用 unique_ptr
在堆上创建 child objects 并且然后 将 那个 unique_ptr
移动到 parent。如果您只是将 operator<<
定义为采用 unique_ptr
:
,则可以对 operator<<
执行相同的操作
Parent &Parent::operator<<(std::unique_ptr<Child> ch) {
children.emplace_back(std::move(ch)); }
现在假设您的 mk
全局函数本质上只是 make_unique
的别名:
template<typename T, typename... TArgs>
std::unique_ptr<T> mk(TArgs&&... mArgs) {
return std::make_unique<T>(std::forward<TArgs>(mArgs)...)); }
您应该能够使用您想要的语法。
(基于我之前的评论)。我建议你构造 base 的 unique_ptr 并将其提供给 opertor<<
(ideone link)。无需花哨或复杂。
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) ;
}
假设我有一个容器 object 存储 std::vector
的多态 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;
}
};
现在我可以像这样使用 Parent
class:
int main()
{
Parent p;
p.add<Child1>(some_args1).add<Child2>(some_args2);
}
虽然这个语法实现了我想做的事情(将 children 的加法链接到单个 parent),但我发现它很难阅读,尤其是在我的实际用例中。
我真的很想改用 operator<<
。但是我想不出一种方法来构建children。
// Desired syntax
int main()
{
Parent p;
p << mk<Child1>(some_args1) << mk<Child2>(some_args2);
}
请注意我从未在 mk
函数中指定 parent。
不想说mk<Child1>(p, some_args1)
。编译器应该从 operator<<
.
p
有什么方法可以实现这个 mk
函数生成的代码等于通过 .add<T>(...)
链接生成的代码吗?
我设法实现它的唯一方法是使用 man-in-the-middle 结构,该结构包含 child class 的构造可变参数.
template<typename T, typename... TArgs> struct DeferCtor
{
std::tuple<TArgs...> ctorArgs;
};
然后operator<<(DeferCtor<T, TArgs...>&)
会处理object在Parent
中的构造。
有没有办法避免这一步,同时仍然具有所需的语法? (不在 mk
函数中传递 parent 实例。)
您并没有真正在现有代码中创建 objects —— 您正在使用 unique_ptr
在堆上创建 child objects 并且然后 将 那个 unique_ptr
移动到 parent。如果您只是将 operator<<
定义为采用 unique_ptr
:
operator<<
执行相同的操作
Parent &Parent::operator<<(std::unique_ptr<Child> ch) {
children.emplace_back(std::move(ch)); }
现在假设您的 mk
全局函数本质上只是 make_unique
的别名:
template<typename T, typename... TArgs>
std::unique_ptr<T> mk(TArgs&&... mArgs) {
return std::make_unique<T>(std::forward<TArgs>(mArgs)...)); }
您应该能够使用您想要的语法。
(基于我之前的评论)。我建议你构造 base 的 unique_ptr 并将其提供给 opertor<<
(ideone link)。无需花哨或复杂。
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) ;
}