2013-04-04 210 views
4

给定多个派生类的基类,目标是创建一个包装类,允许STL容器查看具有基本接口事件的对象,尽管不同的派生类可能实际上是添加到容器中。 (见Retrieve data from heterogeneous std::list)。unique_ptr成员,私人拷贝构造函数与移动构造函数

经过一番修补之后,我想出了一个新的派生类,它是一个基类unique_ptr的包装。然而,移动构造函数让我感到困惑。

class Base { 
    friend class BaseWrapper; 
    virtual Base * clone() const = 0; 
public: 
    virtual ~Base() {} 
    //... public interface 
}; 

class Derived : public Base { 
    //... specific members for derived class 
    Base * clone() const { return new Derived(*this); } 
public: 
    //... implement public interface 
}; 

class BaseWrapper : public Base { 
    std::unique_ptr<Base> ptr_; 
    Base * clone() const { return ptr_->clone(); } 
public: 
    BaseWrapper (const Base &b) : ptr_(b.clone()) {} 
    //... implement public interface by forwarding to ptr_ 
}; 

typedef std::list<BaseWrapper> BaseList; 

int main() { 
    BaseList l; 
    l.push_back(Derived()); 
} 

This does not compile with g++ 4.7.2

现在,为了使用BaseWrapper,我可以实现公众移动构造函数是这样的:

BaseWrapper (BaseWrapper &&bw) { ptr_.swap(bw.ptr_); } 

And this works fine。但是,如果我将其设为私人,it will not compile

然而,我发现不是上述情况,I can instead define a private "copy" constructor(使其成为公众也适用,当然):

BaseWrapper (BaseWrapper &bw) { ptr_.swap(bw.ptr_); } 

有人能告诉我,如果这是这样运作的,以及为什么或者为什么不呢?如果它应该工作,为什么我不能使移动构造函数为私有?

你可以按照this link来更完整地说明上述玩具程序。

+1

我不确定为什么在你的例子中编译器试图使用隐式删除的拷贝ctor,当它应该使用移动ctor。 [例如gcc 4.8](http://liveworkspace.org/code/2cW7iN0.0)编译,但是[clang 3.2它没有](http://liveworkspace.org/code/2fUtBE$0)。 – 2013-04-04 21:49:13

+0

@JesseGood:谢谢。我发现正确使用'unique_ptr'的技巧超过了它的用处,因此我可能会推荐''shared_ptr' over'unique_ptr'。 – jxh 2013-04-04 22:16:57

+0

请注意,删除'virtual〜Base(){}'允许[在g ++ 4.7.2上编译](http://ideone.com/Un5EbY),它可以防止隐式声明移动ctor,并且有一个注释'当移动构造函数未被隐式声明或显式提供时,否则将调用移动构造函数的表达式可能会调用复制构造函数。我仍然不确定谁是对的。 – 2013-04-05 00:25:50

回答

4

[除去错误诊断]

即实际上编译上的gcc 4.8。看起来gcc 4.7将BaseWrapper (const Base &)作为一个拷贝构造函数(实际上不是),并且隐式地删除了移动构造函数(如果它确实是一个拷贝构造函数,这将是预期的行为)。

+0

你需要提供某种方法来将'unique_ptr'推入到列表中。这是一个移动或复制构造函数(采取引用,而不是一个const引用)。这就是为什么你需要定义一个,否则'unique_ptr'成员不能移动到列表中。至于为什么移动构造函数不能私有,我已经回答了上面的问题。 – mfontanini 2013-04-04 21:39:35

+0

这是因为你的拷贝构造函数需要一个'BaseWrapper&'。左值引用无法绑定到临时对象,因此编译器甚至不考虑该构造函数。这就像这样:'int&i = int()'。该引用永远不会绑定到临时的“int”。另一方面,右值引用允许这个。 – mfontanini 2013-04-04 21:44:25

+0

它似乎在gcc 4.8上工作:http://liveworkspace.org/code/3HyLOA$0 – mfontanini 2013-04-04 21:53:08