2014-07-17 61 views
2

我有需要给他们由常量左值(其中将被复制)和右值引用(速度)C++,采取常量左值和右值引用在函数

struct Object { 
    ... 
    Object(Object&& o) { ... } 
    Object(const Object& o) { ... } 
    ... 
}; 

... 

struct SomeClass { 
    ... 
    Object arr[7]; 

    void copy(int pos,const Object& o) { arr[pos] = o; } 
    void copy(int pos,Object&& o) { arr[pos] = o; } 
    ... 
}; 
采取可变能力的几种方法

因此,两个复制方法在SomeClass中是完全相同的。唯一的区别是在一个对象中被传递为const,它将被复制,并且在另一个对象被传递给一个快速拷贝作为右值引用被使用。

这两种方法的代码是完全相同的。

现在,在上面的例子中没有那么悲惨,但是,我的方法有点大了,大约9-15行左右。显然,解决方法是像复制它们一样复制它们,但它感觉不对。

如何重新使用复制方法的代码?

+3

你可以通过值和'std :: move'在函数中获取参数。 – Praetorian

+0

好吧,Object类使用右值引用构造函数来“窃取”o对象的成员变量,并让给定的对象不做任何事情。虽然const Object构造函数不能修改对象,也不能窃取它的变量,所以它必须复制。 –

+0

@Praetorian这是一个好主意。如果我没有找到别的方法而没有复制任何东西,我可能会使用它。 –

回答

2

通用参考和std::forward允许您实现完美转发。这里的修改copy

template <typename T> 
void copy(int pos, T&& o) {arr[pos] = std::forward<T>(o);} 

如果你担心其他类型Object可能隐式转换,也可以在copy正文中添加一个static_assert。如果您将l值Object传递给copy,则T将推断为Object&std::forward<Object&>返回一个参考。所以,复制分配过载将被选中。如果您通过r值Objectcopy,T将推断为Objectstd::forward<Object&>返回Object&&。由于std::forward<Object>(o)也是一个右值,移动赋值操作符将被选中。

+0

完美!我只是难过地采取另一个最好的答案,并把它给你:( –

3

首先,你在这两种情况下做一个拷贝赋值:

void copy(int pos,const Object& o) { arr[pos] = o; } 
void copy(int pos,Object&& o) { arr[pos] = o; } 

如果它有一个名字,这是一个左值。在第二个函数中,o有一个名称,所以它是一个左值(尽管是右值引用)。你想要arr[pos] = std::move(o);

通常的方式,以避免不必编写copy两次是通过值采取o和从它移动:

void copy(int pos, Object o) { arr[pos] = std::move(o); } 

o将如果传递左值被复制构造,然后将分配到arr[pos] 。如果你传递一个右值,o将被移动构造,然后进一步移动 - 分配到数组中。所以你仍然要避免在右值情况下的副本,但你在这两种情况下支付额外的移动。如果移动像他们应该那样便宜,它不会有太多的额外开销。

但是,请注意,这不适用于不支持移动语义的遗留类型。在这种情况下,这个函数将复制你传递的内容,然后从中复制分配,而两个引用的重载只会执行一个副本。

+0

那么,你建议用这个替换这两个?但是,如果SomeClass的用户在调用副本时想要自己使用std :: move,该怎么办? sc.copy(0,std :: move(tempobj)); 或者我没有正确理解你? –

+0

哦,好吧,我明白了,这个变体构造了两次右值。我喜欢! –

+0

@VanillaFace如果你像'std :: move'那样传递一个右值,那么'o'将会移动,然后移动到数组中。 –