C++11 提供移动参数的构造函数的惯用方法
假设我有以下课程:C++11 提供移动参数的构造函数的惯用方法,c++11,move-semantics,rvalue-reference,C++11,Move Semantics,Rvalue Reference,假设我有以下课程: #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; }; #包括 福班 { 公众: Foo(const std::vector&a,c
#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;
};
#包括
福班
{
公众:
Foo(const std::vector&a,const std::vector&b)
:a{a},b{b}{
私人:
std::载体a,b;
};
但是现在我想解释一下,在这种情况下,构造函数的调用者可能会将临时变量传递给它,我想将这些临时变量适当地移动到a
和b
现在我真的必须再添加3个构造函数,其中1个有a
作为右值引用,1个有b
作为右值引用,1个只有右值引用参数
当然,这个问题可以推广到任何数量的值得移动的参数,并且所需的构造函数的数量将是参数^2^个参数
这个问题也适用于所有函数
这样做的惯用方式是什么?还是我完全忽略了一些重要的内容?通常的方法是按值传递,然后从参数中移动并构造成员:
Foo(std::vector<int> a, std::vector<int> b)
: a{ std::move(a) },
b{ std::move(b) }
{}
Foo(标准::向量a,标准::向量b)
:a{std::move(a)},
b{std::move(b)}
{}
如果需要副本,则调用者将创建副本,然后将其从移动到构造成员。如果调用方传递了一个临时值(或其他右值),则不会进行复制,只进行一次移动
对于没有有效移动构造函数的参数,那么接受对const的引用稍微有效一些,我会保留这一点
如果函数不需要传递值的副本,则所有这些都不适用-如果不修改该值并且不需要它在函数执行结束后继续使用const ref。就我个人而言,我在构造函数中使用按值传递并自由移动,但在其他函数中很少使用。通常的方法是按值传递,然后从参数中移动构造成员:
Foo(std::vector<int> a, std::vector<int> b)
: a{ std::move(a) },
b{ std::move(b) }
{}
Foo(标准::向量a,标准::向量b)
:a{std::move(a)},
b{std::move(b)}
{}
如果需要副本,则调用者将创建副本,然后将其从移动到构造成员。如果调用方传递了一个临时值(或其他右值),则不会进行复制,只进行一次移动
对于没有有效移动构造函数的参数,那么接受对const的引用稍微有效一些,我会保留这一点
如果函数不需要传递值的副本,则所有这些都不适用-如果不修改该值并且不需要它在函数执行结束后继续使用const ref。就个人而言,我在构造函数中大量使用传递值和移动,但在其他函数中很少使用。实际上,如果移动构造非常便宜,您应该按值移动 这会导致在每种情况下都比理想情况多移动一次 但如果你真的必须避免,你可以这样做:
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);
}
};
或
模板
测试(嘈杂和nin):n(标准::前向(nin)){}
与版本的接收器具有相同的复制/移动次数
(Noised
是一种打印有关其移动/复制内容的信息的类型,因此您可以看到通过省略优化的内容)
只有当额外的移动
非常重要时,才值得这样做。对于向量
,它不是
此外,如果你有一个“真正的临时”你通过,按价值的一个是一样好的水槽或“完美”的。真的,你应该采取的价值,如果移动建设是非常便宜的 这会导致在每种情况下都比理想情况多移动一次 但如果你真的必须避免,你可以这样做:
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);
}
};
或
模板
测试(嘈杂和nin):n(标准::前向(nin)){}
与版本的接收器具有相同的复制/移动次数
(Noised
是一种打印有关其移动/复制内容的信息的类型,因此您可以看到通过省略优化的内容)
只有当额外的移动
非常重要时,才值得这样做。对于向量
,它不是
此外,如果您要传递一个“真正的临时”参数,则按值传递的参数与按值传递的参数或“完美”参数一样好。我看到的一个解决方案是按值获取这两个参数,然后移动它们。虽然我看到了这一争议,但我再也找不到来源了:/哦,是的,你是对的。我将编辑我的答案:p。我看到的一个解决方案是按值获取两个参数,然后移动它们。虽然我看到了这一争议,但我再也找不到来源了:/哦,是的,你是对的。我将编辑我的答案:p.临时变量确实会被移动,但用左值调用这个ctor会稍微慢一点,因为它是被复制和移动的,而不是被复制的?稍微慢一点,但想法是移动构造预计会非常便宜。对于
std::vector
,您将很难测量差异。而且与编写2^n个不同的构造函数相比,它的难度要小得多,也不容易出错!这种方法对于vector
来说是一个不错的选择,因为移动与复制相比非常便宜。只是要小心将这种方法推广到其他可能没有这种特性的类型。@Jupiter:没有一种正确的方法可以做到这一点。对于每种情况,我都会计算拷贝数和移动数,并估计每种拷贝数和移动数的成本。有3种方法,每种方法都有优点和缺点。这一个,2^n解决方案(对于单参数来说非常好)和约束模板解决方案(性能完美,但读写不好,容易出错,而且常常是杀伤力过大)。对于vector
,我建议采用按价值计算的方法。翻新旧房子通常比从头开始建造新房子更困难、更昂贵。这需要不同的技能组合,结果也不一样。但有时翻新是唯一可行的答案,尤其是如果你
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)) {}