在构造期间使用 std::tuple 成员引用?

Use std::tuple member references during construction?

#include "MassivePOD.h"
#include "DependantClass.h" // Constructor: `DependantClass(MassivePOD&)`

class ExplicitSolution
{
public:
    ExplicitSolution() : m_pod{...}, m_dep{m_pod} { };
private:
    MassivePOD  m_pod;
    DependantClass  m_dep;
};

template<typename... T>
class TemplatedSolution
{
public:
    template<typename... TupArgs> TemplatedSolution(TupArgs...);
private:
    // Can assume in-order construction works
    std::tuple<T...>  m_tuple;
};


int main()
{
    ExplicitSolution a{}; // Easy!
    TemplatedSolution<MassivePOD, DependantClass> b{
        std::forward_as_tuple(...) // Forwarded to MassivePOD constructor
      , std::forward_as_tuple(???) // Forwarded to DependantClass constructor
        }; // Hard?
}

我希望这个例子能说明我的问题。我想在构建整个 std::tuple 之前引用一个较早构建的 std::tuple 成员。有一个优雅的解决方案吗?我知道 可能 使用 void * hackery,但我宁愿在走那条黑暗孤独的道路之前寻求一些帮助。

想法 1

我试过创建一个 get_ref 函数,但我遇到了同样的问题,我无法访问尚未创建的对象的成员函数。不过,这是我的尝试之一。

#include <tuple>
#include <utility>

class Foo // simplified base-case
{
public:
    constexpr Foo(int x, char y, int& z) : m_tup{x, y, z*5.0} { };

    constexpr int& get_int() { return std::get<0>(m_tup); };
    constexpr char& get_char() { return std::get<1>(m_tup); };
    constexpr double& get_double() { return std::get<2>(m_tup); };

private:
    std::tuple<int, char, double>  m_tup;
};

int main()
{
    auto super = Foo(5, 'a', ::get_int()); // ???
}

Demo

想法 2

然后我想也许我可以做一些像 std::functionstd::place_holders 所做的事情,所以一个包含 pointer/reference 的静态对象指向 [=13= 的每个元素] 将会。我认为这值得尝试,但我不知道如何在实践中实施它...

使用 std::shared_ptr 并将其作为 std::shared_ptr 存储在 TupleWrapper 中怎么样?

auto p_first_member_tmp = std::make_shared<MassivePOD>(
    std::forward_as_tuple(...));

TupleWrapper<std::shared_ptr<MassivePOD>, DependantClass> wrapper{
    p_first_member_tmp, 
    std::forward_as_tuple(*p_first_member_tmp, ...)}

这是一个额外的间接级别,但至少不会复制或移动昂贵的复制或移动结构。

我在考虑为什么这在像 Python 这样的语言中从来都不是问题,这是因为对象只创建一次,然后其他所有内容都指向它 - 这与这里的这种方法近似。您也不必额外跟踪对象的生命周期,因为 shared_ptr 已经为您完成了。

另一个建议:元组class,不是为成员的构造函数获取参数,而是接受接收正在构造的实例和return成员的函数。然后,这些函数可以通过 getter 引用先前构造的成员。您所要做的就是确保早期成员的 getter 有效,并且如果在构建后期成员时被调用,则不会调用未定义的行为。

这里有一个为两个元素硬编码的例子来演示这个原理。我将把它留给你让它与 n 元素一起工作。请注意,基本上您将在这里重新实现一个元组 - 我不确定您是否可以使用现有的 std::tuple.

template<typename A, typename B>
class TemplatedSolution
{
public:
    template<typename AF, typename BF> TemplatedSolution(AF af, BF bf)
        : a(af(*this))
        , b(bf(*this))
    {
    }

    A& get_first() {
        return a;
    }

    B& get_second() {
        return b;
    }

private:
    A a;
    B b;
};

用法:

typedef TemplatedSolution<MassivePOD, DependantClass> ClassT;

ClassT b{
    [](ClassT& res) { return MassivePOD(); },
    [](ClassT& res) { return DependantClass(res.get_first()); },
};

由于 return 值优化,MassivePOD 将在 TemplatedSolution 实例中正确构建。

演示没有副本的完整示例可用 here