我正在寻找一种通过工厂函数返回值初始化结构中不可移动成员的方法。具体而言,该语言承认通过聚合初始化初始化不可移动成员的情况,但是存在不可能进行这种初始化的情况。我想知道在不允许聚合初始化的情况下是否还有一种方法可以直接(就地)初始化不可移动的成员。
这是similar question,但它处理的是从函数返回非可复制/可移动类型初始化非静态成员的情况。
一个例子是有序的。请考虑以下事项:
struct S { S(S&&) = delete; }; // Non-moveable type.
S F() { return {}; }
现在,请考虑从返回值开始在聚合中对成员进行以下初始化。
struct T { S s; };
void G() { T t{F()}; } // OK (in-place construction of t).
上述工作原因是t
正在通过聚合初始化进行初始化。 S::S(S&&)
没有参与。 [注意:此摘录仅适用于GCC-7,而不适用于GCC-6或旧版本。]
但是如果根本不可能进行聚合初始化呢?
struct X {};
struct U: virtual X // Not an aggregate anymore.
{ S s; U(S&& s): s{(S&&)s} {} }; // Needs an explicit constructor.
void H() { U u{F()}; } // Doesn't work (S::S(S&&) is deleted).
......或者只是不方便?
struct A {}; struct B {}; struct C {};
struct V: A, B, C { S s; }; // Still an aggregate.
void I() { V v{F()}; } // No chance.
void J() { V v{{}, {}, {}, F()}; } // Kinda works, but ugly.
这里的要点是,尽管S
处于不可移动状态,但如果它可以用作在与上述情况类似的情况下就地构造的成员对象,那将是很好的。
有可能吗?
答案 0 :(得分:4)
当你让一个类不动时,你已经使它不动。这意味着你必须在prvalue,直接使用构造函数调用或直接使用braced-init-list的情况下构建它。
您无法将该类型的对象传递给其他人。如果您希望U
能够构建S
成员,则需要让U
的构造函数直接或间接提供用于初始化S
对象的参数原位。例如:
struct U // Constructors mean that it's not an aggregate.
{
S s;
template<typename ...Args>
U(Args &&...args)
: s(std::forward<Args>(args)...) {}
};
void H() { U u{}; } //Passes no parameters to `S`'s construction.
如果您确切知道要在构造函数中使用的成员的构造函数,那么您不需要像这样使用转发。
以上是有效的,因为t是通过聚合初始化来初始化的。不涉及S :: S(S&amp;&amp;)。 [注意:此摘录仅适用于GCC-7,而不适用于GCC-6或旧版本。]
更正:上述工作在GCC-7中,因为它支持保证省略,这使得这成为可能。如果没有保证省略,prvalue将代表复制/移动的临时值。即使编译器忽略了它,编译器仍然必须确保代码在复制/移动时能够正常工作。
我正在寻找一种通过工厂函数返回值初始化结构中不可移动成员的方法。
这是一个相当不同的问题,但保证省略使其运作良好。只需将函数传递给构造函数:
struct U
{
S s;
template<typename Func>
U(Func f) : s(f()) {}
};
如果您的工厂函数接受参数,那么您需要传递一个lambda来捕获这些参数并将捕获的值传递给工厂函数。
答案 1 :(得分:0)
当你返回一个S实例局部变量时,它会变成一个r-xvalue,它首先看移动构造函数。如果它被删除,那么它将无效。
同样来自标准: N4659 15.8.1 /(10.4)复制/移动构造函数[class.copy.ctor]
删除的移动构造函数否则会干扰 从rvalue初始化,可以使用复制构造函数