我的类的构造函数初始化引用成员。我怎样才能安全地传递临时对象以初始化这些引用成员?
我有一个对象类和对这些对象执行的操作类。这些操作都派生自提供公共(public)接口(interface)的抽象基类。否则,这些操作可能包含特定实现的大量附加信息。
class Object;
class Operation {
public:
virtual void do_something( Object& obj ) = 0;
/* ... */
}
class FooOperation : public Operation { /*...*/ }
class BarOperation : public Operation { /*...*/ }
class BazOperation : public Operation { /*...*/ }
我也想组合操作。一种正确的方法是使用以下类:
class ComposedOperation : Operation {
private:
const Operation& m_op1; const Operation& m_op2;
public:
ComposedOperation( const Operation& op1, const Operation& op2 )
: m_op1(op1), m_op2( op2 ) { };
void do_something( Object& obj ) override
{ m_op1.do_something(obj); m_op2.do_something(obj); }
}
问题在于组合操作只能绑定(bind)左值引用。像下面这样的组合是危险的:
auto op = ComposedOperation( BarOperation(), ComposedOperation( FooOperation(), BazOperation() ) );
如何让类知道第一个/第二个操作是左值还是右值?最好不要重载构造函数。我想知道模板是否可以提供帮助。
最佳答案
我认为有两种选择:
将所有操作存储在与树分开的某个地方,并确保它们的生命周期比对它们的引用更长。
让每个 Operation 拥有自己的 child ,并按值或在
unique_ptr
中存储它们。
选项 1 在某些情况下可能有用(如果您在某处有一个操作注册表,并且 Operation
树只是一个组织的东西)。但是,从您的使用示例来看,情况似乎并非如此。
选项 2 绝对更简单,实现移动语义应该可以避免任何性能问题。
如果你真的需要在特定实例中避免复制/移动,你可以创建一个 Ref
包装器,它通过引用存储一个 Operation
,并实现 Operation
界面本身。使用它时,您显然需要自己确保正确的生命周期。
所以(大约):
template<class A, class B>
struct FooOperation {
A a; B b;
// ... implement operation concept
};
template<class T>
struct Ref {
T& t;
// ... implement operation concept
};
auto a = BarOperation();
auto b = FooOperation<Ref<A>, B>(Ref(a), BazOperation());
或者如果我们需要运行时多态性:
struct FooOperation : Operation {
std::unique_ptr<Operation> a, b;
// ... implement operation interface
};
struct Ptr : Operation {
Operation* t;
// ... implement operation interface
};
auto a = std::make_unique<BarOperation>();
auto b = FooOperation(std::make_unique<Ptr>(a), std::make_unique<BazOperation>());
关于C++模板成员初始化: move-construction with rvalue but reference with lvalue,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57964380/