避免使用 `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) ;
}