避免使用"运算符<<推迟"child"对象构造

Avoiding to defer "child" object construction with `operator<<`

本文关键字:lt child 对象 推迟 运算符      更新时间:2023-10-16

假设我有一个容器对象,它存储了多态子项的std::vector

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 类:

int main()
{
    Parent p;
    p.add<Child1>(some_args1).add<Child2>(some_args2);
}

虽然这种语法实现了我想做的事情(将子项添加到单个父级),但我发现它很难阅读,尤其是在我的实际用例中。

我真的很想改用operator<<。但是我想不出一种方法来构建孩子。

// Desired syntax
int main()
{
    Parent p;
    p << mk<Child1>(some_args1) << mk<Child2>(some_args2);
}

请注意,我从未在 mk 函数中指定父级。

我不想说mk<Child1>(p, some_args1).编译器应该从operator<<的链接中找出p

有什么方法可以实现这个mk函数生成与通过.add<T>(...)链接生成的代码相同的代码?

我设法实现这一点的唯一方法是使用中间结构,该结构保存子类的构造可变参数。

template<typename T, typename... TArgs> struct DeferCtor
{
    std::tuple<TArgs...> ctorArgs;
};

然后operator<<(DeferCtor<T, TArgs...>&)将处理对象在Parent内部的构造。

有没有办法避免此步骤,同时仍然具有所需的语法?(不在 mk 函数中传递父实例。

你并没有真正在现有代码中就地创建对象 - 你使用unique_ptr在堆上创建子对象,然后将该unique_ptr移动到父对象中。 你可以对你的operator<<做同样的事情,如果你只是把它定义为采取一个unique_ptr

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)...)); }

您应该能够使用所需的语法。

(基于我之前的评论)。我建议你构建碱基的unique_ptr并将其提供给opertor<<(ideone链接)。无需花哨或复杂。

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) ;
}