按值传递vs const&和&&过载 [英] Passing by value vs const & and && overloads

查看:247
本文介绍了按值传递vs const&和&&过载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,在查找move语义后,我看到一般的共识是,当你打算转让所有权时,通过价值。但在 Scott Meyer关于通用参考我注意到 std :: vector :: push_back 有2个重载:

  void push_back(const T& value); 
void push_back(T&& value);

所以我想到了自己,不会 void push_back ; 足够了吗?我问了几个人,最终导致以下测试用例:

  #include< memory> 
#include< iostream>
#include< type_traits>

struct A
{
A(){std :: cout< A Default constructor\\\
; }
A(const A&){std :: cout<< A Copy\\\
; }
A(A&&){std :: cout<< A Move\\\
; }
};

std :: aligned_storage< sizeof(A)>内容;
A& alias = * reinterpret_cast< A *>(& contents);

void ByVal(A a)
{
new(& contents)A(std :: move(a));
alias。〜A();
}

void ByLCRef(A const& a)
{
new(& contents)A(a);
alias。〜A();
}

void ByRRef(A& a)
{
new(& contents)A(std :: move(a));
alias。〜A();
}

int main()
{
A a;
std :: cout<< \\\
;
std :: cout<< ByVal(a); \\\
;
ByVal(a);
std :: cout<< ByVal(std :: move(a)); \\\
;
ByVal(std :: move(a));
std :: cout<< ByVal(A()); \\\
;
ByVal(A());
std :: cout<< ByLCRef(a); \\\
;
ByLCRef(a);
std :: cout<< ByRRef(std :: move(a)); \\\
;
ByRRef(std :: move(a));
std :: cout<< ByRRef(A()); \\\
;
ByRRef(A());
}

这将产生以下结果:

  A默认构造函数

ByVal(a);
A复制
A Move
ByVal(std :: move(a));
A Move
A Move
ByVal(A());
默认构造函数
移动
ByLCRef(a);
A复制
ByRRef(std :: move(a));
A Move
ByRRef(A());
默认构造函数
A移动

正如你所看到的, ByVal 产生与参考重载对相比的额外移动。所以问题是:它值得吗?什么时候创建两个重载而不是一个简单的值传递函数?

解决方案


参见,ByVal产生一个额外的移动相比对
引用重载。所以问题是:它值得吗?
何时会创建两个重载而不是一个简单的值传递函数?


+1大多数提问者不用费心做分析。所以你得到我的upvote做自己的家庭作业。 : - )



它是否值得取决于移动构造函数的成本,以及函数需要多少参数。在一个极端,如果移动构造函数不是那么快,你可能会关心消除它们(喜欢const&&&& amp; overload解决方案)。在另一个极端,如果你的函数有4个参数,每个参数需要左值/右值处理,你可能不愿意写16个重载覆盖所有的情况。这需要维护很多代码,固有的代码复杂性是对错误的邀请。因此,按值方法看起来更有吸引力(这不需要重载)。



所以imho,没有一般的答案,是值得的问题。最好的答案是让你知道每个解决方案的成本,你已经做了,并根据具体情况作出工程判断。



更新



向量< T> :: push_back ;,&&超载解决方案是值得的。只有一个参数,我们不知道移动构造函数有多昂贵。事实上,我们甚至不知道如果有一个移动构造函数。修改实验以测试后一种情况(移除move构造函数):

  ByVal(a); 
A复制
A复制

ByLCRef(a);
A复制

您要支付一份还是两份复制 A 到向量



你对参数的了解越少,越需要倾向于性能方面,尤其是如果你正在编写一些像 std :: vector p>

So after looking up move semantics I see that general consensus is to pass by value when you intend to transfer ownership. But in Scott Meyer's talk on Universal references I've noticed that std::vector::push_back has 2 overloads:

void push_back( const T& value );
void push_back( T&& value );

So I thought to myself, wouldn't void push_back( T value ); be enough? I've asked a few people which ultimately lead to the following test case:

#include <memory>
#include <iostream>
#include <type_traits>

struct A
{
    A() { std::cout << "A Default constructor\n"; }
    A(const A &) { std::cout << "A Copy\n"; }
    A(A &&) { std::cout << "A Move\n"; }
};

std::aligned_storage<sizeof(A)> contents;
A& alias = *reinterpret_cast<A*>(&contents);

void ByVal(A a)
{
    new (&contents) A(std::move(a));
    alias.~A();
}

void ByLCRef(A const& a)
{
    new (&contents) A(a);
    alias.~A();
}

void ByRRef(A&& a)
{
    new (&contents) A(std::move(a));
    alias.~A();
}

int main()
{
    A a;
    std::cout << "\n";
    std::cout << "ByVal(a);\n";
    ByVal(a);
    std::cout << "ByVal(std::move(a));\n";
    ByVal(std::move(a));
    std::cout << "ByVal(A());\n";
    ByVal(A());
    std::cout << "ByLCRef(a);\n";
    ByLCRef(a);
    std::cout << "ByRRef(std::move(a));\n";
    ByRRef(std::move(a));
    std::cout << "ByRRef(A());\n";
    ByRRef(A());
}

Which produces the following:

A Default constructor

ByVal(a);
A Copy
A Move
ByVal(std::move(a));
A Move
A Move
ByVal(A());
A Default constructor
A Move
ByLCRef(a);
A Copy
ByRRef(std::move(a));
A Move
ByRRef(A());
A Default constructor
A Move

As you can see, ByVal produces 1 extra move compared to pair of reference overloads. So the question is: is it worth it? When would you create two overloads instead of one simple pass by value function?

解决方案

As you can see, ByVal produces 1 extra move compared to pair of reference overloads. So the question is: is it worth it? When would you create two overloads instead of one simple pass by value function?

+1 Most people who ask this question don't bother to do the analysis. So you get my upvote for doing your own homework. :-)

Whether it is worth it or not is going to depend on the cost of the move constructor, and on how many arguments the function takes. On one extreme, if the move constructor isn't that fast, you may care a lot about eliminating them (favoring the const&, && overload solution). At the other extreme, if your function has 4 parameters, each of which need lvalue/rvalue treatment, you may not be willing to write 16 overloads to cover all the cases. That's a lot of code to maintain, and the inherent code complexity is an invitation for bugs. So the by-value approach looks more attractive (which requires no overloads).

So imho, there is no general answer to the "is it worth it" question. The best answer is to equip yourself with the knowledge about the cost of each solution, as you have already done, and make an engineering judgement on a case by case basis.

Update

In the case of vector<T>::push_back imho the const&, && overload solution is worth it. There is only one parameter, and we have no idea how expensive the move constructor is. Indeed, we don't even know if there is a move constructor. Modifying your experiment to test out that latter case (removing the move constructor):

ByVal(a);
A Copy
A Copy

ByLCRef(a);
A Copy

Do you want to pay one copy or two to copy your A into the vector?

I.e. the less you know about your parameters, the more you have to lean towards the performance side, especially if you're writing something as heavily used as std::vector.

这篇关于按值传递vs const&amp;和&amp;&amp;过载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆