2017-08-31 123 views
1

可以说我有下面的类:习惯的方法

#include <vector> 
class Foo 
{ 
public: 
    Foo(const std::vector<int> & a, const std::vector<int> & b) 
     : a{ a }, b{ b } {} 
private: 
    std::vector<int> a, b; 
}; 

但现在我要考虑到在构造函数的调用者可能传递的临时给它和我的情况想要将这些临时对象移动到ab

现在我真的必须再添加3个构造函数,其中1个具有a作为右值引用,其中1个具有b作为右值引用,1只具有右值引用参数?

当然这个问题推广到任何值得移动的参数,并且所需构造函数的数量将是参数^ 2 2 ^参数。

这个问题也推广到所有功能。

这样做的习惯做法是什么?还是我完全错过了重要的东西?

+0

我见过的一种解决方案是按值取两个参数,然后移动它们。我已经看到了这个争议,但我没有源:/ – Quentin

+0

哦,是的,你是对的。我将编辑我的答案:p。 – Jupiter

+0

(相关问题)(/ q/2839127) –

回答

2

真的,如果移动建设非常便宜,你应该采取价值。

这导致在每种情况下,理想情况下只有一个额外的移动。

但如果你真的必须避免这种情况,你可以这样做:

template<class T> 
struct sink_of { 
    void const* ptr = 0; 
    T(*fn)(void const*) = 0; 
    sink_of(T&& t): 
    ptr(std::addressof(t)), 
    fn([](void const*ptr)->T{ 
     return std::move(*(T*)(ptr)); 
    }) 
    {} 
    sink_of(T const& t): 
    ptr(std::addressof(t)), 
    fn([](void const*ptr)->T{ 
     return *(T*)(ptr); 
    }) 
    {} 
    operator T() const&& { 
    return fn(ptr); 
    } 
}; 

它采用视网膜静脉阻塞/省音,以避免在一堆基于指针的开销和类型擦除的费用,额外的举动。

Here是一些测试代码,以证明

test(noisy nin):n(std::move(nin)) {} 
test(sink_of<noisy> nin):n(std::move(nin)) {} 

通过noisy的正好1举动构建体不同。

“完美” 的版本

test(noisy const& nin):n(nin) {} 
test(noisy && nin):n(std::move(nin)) {} 

template<class Noisy, std::enable_if_t<std::is_same<noisy, std::decay_t<Noisy>>{}, int> = 0 > 
test(Noisy && nin):n(std::forward<Noisy>(nin)) {} 

具有相同数量的复制/移动作为sink_of版本。

noisy是打印什么移动/复制它从事,所以你可以看到什么获取通过省音优化掉的信息的类型)

这是唯一值得当额外move是重要的消除。对于vector它不是。另外,如果你有一个“真正的临时”,你正在通过,那么按值的一个和sink_of或“完美”的一样好。

4

通常的方法是向传值,然后从参数移动-构造成员:

如果需要复制,它会被调用者创建,然后移入从构建成员。如果调用者通过临时(或其他右值),则不进行复制,只进行一次移动。

对于没有高效移动构造函数的参数,接受对const的引用会稍微有效一些,我会保留这一点。

如果函数不需要复制传递的值,则不适用 - 如果不修改该值并且不需要它超过结束时的活动,则继续使用const ref函数执行。就我个人而言,我在构造函数中大量使用传值和移动,但很少使用其他函数。

+0

是不是真的临时会被移动,但用左值调用这个ctor会稍微慢一些,因为它被复制和移动而不是被复制? – Jupiter

+1

稍微有一点,但想法是移动建筑预计非常便宜。对于'std :: vector',你会很难推测出差异。与编写2^n个不同的构造函数相比,它更简单,更容易出错! –

+1

这种方法对于'矢量'来说是一个很好的选择,因为与拷贝相比,移动是如此便宜。只要小心将这种方法推广到其他类型,您可能没有这个特征。 –