2015-09-09 54 views
3

作为新的右值引用和移动语义,我试图从std::vector派生一个简单的模板类(我保证我会小心缺乏虚拟析构函数)。以“继承”的举动构造函数,我只是调用基类的同行和std::move“继承”移动操作无需访问基类成员变量

template <typename T> 
class Vec: public std::vector<T> { 
    public: 
    ... 
    //move ctors 
    Vec(Vec&& v): std::vector<T>(v) { //move extra members with std::move } 
    explicit Vec(std::vector<T>&& v): std::vector<T>(v) { //same as above } 
    ... 
}; 

根据我的C++的理解,这似乎值得信赖。然而,我在我的移动赋值运算符的做法少了很多信心,

//assignment 
    Vec& operator=(const Vec& v) { 
     std::vector<T>::operator=(v); 
     if (&v != this) 
      //copy extra members 
     return *this; 
    } 
    //move assignment 
    Vec& operator=(Vec&& v) { 
     std::vector<T>::operator=(v); 
     //move extra members with std::move 
     return *this; 
    } 

这是实现我想要什么的万无一失的方法?在良好实践方面是否有更好的选择?

+2

一个很好的指导原则是[规则零](http://flamingdangerzone.com/cxx11/rule-of-zero/) –

+1

@JonathanWakely尽管我仍然更喜欢在每个地方写'= default'和'= delete'。 – Barry

+0

@巴里,是的,我也是,但我认为仍然遵循规则,只是更明确地表明你有意识地不自己定义成员。 –

回答

4

这似乎是值得信赖的...

似乎值得信赖。但事实并非如此。你看,这个:

Vec(Vec&& v) 

是一个移动构造函数。所以它会被右值调用。但一旦我们在这里,v是一个左值!经验法则:如果它有一个名字,那是一个左值。因此,这部分:

: std::vector<T>(v) 

不叫移动构造std::vector。它调用拷贝的构造函数。您需要显式转换v到右值做正确的事:

Vec(Vec&& v) : std::vector<T>(std::move(v)) { } 
explicit Vec(std::vector<T>&& v): std::vector<T>(std::move(v)) { } 

或者,更好:

Vec(Vec&&) = default; 

同样,写赋值运算符的万无一失的方法就是default -ing他们:

Vec& operator=(Vec const&) = default; 
Vec& operator=(Vec&&) = default; 

但如果你真的有特殊的逻辑,一定要记得转换为右值有太多:

Vec& operator=(Vec&& v) { 
    std::vector<T>::operator=(std::move(v)); 
    // stuff 
    return *this; 
} 

或者甚至更好,将您的特殊逻辑移动到一个自包含的单元中,以便default -ing仍然正确。

+1

@downhillFromHere'default'显式地执行任何函数的默认版本。如果你有成员变量,'default'赋值运算符将是一个成员明智的赋值。如果你需要做资源管理,你应该封装,以便你可以利用零规则。 – Barry