通常,如果您可以foo_t
在某个类的构造函数主体中构造 a(没有成员初始化列表),那么您可以修改您的代码,以便您的类现在具有一个foo_t
属性,并且它的构造函数可以委托构造或在其成员初始化列表中构造它.
基本上,在大多数情况下,您可以重写有问题的构造函数,以便它委托给另一个构造函数,同时为它提供必要的信息以foo_t
在成员初始化程序列表中构造一个实例(我在评论中使用以下“示例”快速而非正式地说明了这一点)https://ideone.com/ubbbb7)
更一般地说,如果元组构造由于某种原因碰巧成为问题,则以下转换将(通常)起作用。诚然,它有点长(而且丑陋),但请记住,这是为了一般性,并且在实践中可能会简化事情。
假设我们有一个构造函数,我们在其中构造 a foo_t
,为了简单起见,我们将进一步假设它具有以下形式:
C::C(T1 arg_1, T2 arg_2) {
side_effects(arg_1, arg_2);
TL1 local(arg_1, arg_2);
second_side_effects(arg_1, arg_2, local);
foo_t f(arg_1, arg_2, local); // the actual construction
final_side_effects(arg_1, arg_2, local, f);
}
函数调用的地方可能会改变参数。我们可以委托一次以消除local_1
构造函数主体中的声明,然后再次摆脱对second_side_effects(arg_1, arg_2, local)
.
C::C(T1 arg_1, T2 arg_2)
: C::C(arg_1, arg_2
,([](T1& a, T2& b){
side_effects(a, b);
}(arg_1, arg_2), TL1(a, b))) {}
C::C(T1& arg_1, T2& arg_2, TL1&& local)
: C::C(arg_1, arg_2
,[](T1& a, T2& b, TL1& c) -> TL1& {
second_side_effects(a, b, c);
return c;
}(arg_1, arg_2, local)) {}
C::C(T1& arg_1, T2& arg_2, TL1& local) {
foo_t f(arg_1, arg_2, local); // the actual construction
final_side_effects(arg_1, arg_2, local, f);
}
活生生的例子
显然,f
可以将其设为 C 的实际成员,并在最后一个构造函数的成员初始化列表中构造。
可以概括为任意数量的局部变量(和参数)。然而,我假设我们的初始构造函数没有任何成员初始值设定项列表。如果它有一个,我们可能需要:
- 在它们发生变异之前复制一些 initial
arg_i
并沿着构造函数链传递副本,以便它们最终可以用于构造成员初始化器列表中的其他成员
- 预构造成员的实例并将它们沿构造函数链传递,以便它们最终可用于移动构造成员初始化器列表中的实际成员
如果由于某种原因,成员的构造函数会产生副作用,则必须选择后者。
然而,有一种情况是这一切都崩溃了。让我们考虑以下场景:
#include <memory>
struct state_t; // non copyable, non movable
// irreversible function that mutates an instance of state_t
state_t& next_state(state_t&);
struct foo_t {
foo_t() = delete;
foo_t(const foo_t&) = delete;
foo_t(const state_t&);
};
// definitions are elsewhere
class C {
public:
struct x_first_tag {};
struct y_first_tag {};
// this constructor prevents us from reordering x and y
C(state_t& s, x_first_tag = {})
: x(new foo_t(s))
, y(next_state(s)) {}
// if x and y were both constructed in the member initializer list
// x would be constructed before y
// but the construction of y requires the original s which will
// be definitively lost when we're ready to construct x !
C(state_t& s, y_first_tag = {})
: x(nullptr)
, y(s) {
next_state(s);
x.reset(new foo_t(s));
}
private:
std::unique_ptr<foo_t> x; // can we make that a foo_t ?
foo_t y;
};
在那种情况下,我承认我不知道如何重写这个类,但我认为它很少见,并不重要。