问题描述
假设我有下面的类,它有一个方法 set_value
。哪个实施更好?
class S {
public:
// set_value method
private:
Some_type value;
};
按值传递,然后移动
void S :: set_value(Some_type value)
{
this-> value = std :: move(value);
}
定义两个重载方法
void S :: set_value(const Some_type& value)
{
this-> value = value;
}
void S :: set_value(Some_type& value)
{
this-> value = std :: move(value);
}
第一种方法需要定义一个方法, / p>
但是,第一种方法似乎效率较低:
- 复制/移动 移除赋值
- 参数的析构函数
对于第二种方法,只执行一个赋值操作。
- Copy/Move constructor for the parameter depending on the argument passed
- Move assignment
- Destructor for the parameter
- Copy/Move assignment depending on which overloaded method is called
那么,哪个实现更好呢?
还有一个问题:下面的代码是否等同于第二种方法中的两个重载方法?
模板< class T>
void S :: set_value(T&& value)
{
this-> value = std :: forward< T&
}
(优化远离)副本,即使这样做会有副作用。因此,传递值和移动结果实际上提供了双方法解决方案的所有性能优势,同时仅提供一个代码路径来维护。
这里有一个例子来证明这一点:
code> #include< iostream>
struct XYZ {
XYZ(){std :: cout< 构造< std :: endl; }
XYZ(const XYZ&){
std :: cout<< 拷贝构建<< std :: endl;
}
XYZ(XYZ&&)noexcept {
try {
std :: cout< 移动构建< std :: endl;
}
catch(...){
}
}
XYZ& operator =(const XYZ&){
std :: cout< assigned<< std :: endl;
return * this;
}
XYZ& operator =(XYZ&&){
std :: cout< move-assigned<< std :: endl;
return * this;
}
};
struct holder {
holder(XYZ xyz):_xyz(std :: move(xyz)){}
void set_value(XYZ xyz){_xyz = std :: move(xyz); }
void set_value_by_const_ref(const XYZ& xyz){_xyz = xyz; }
XYZ _xyz;
};
using namespace std;
auto main() - > int
{
cout<< **创建命名源以供以后使用**< endl
XYZ xyz2 {};
cout<< \\\
** initial construction **<< std :: endl;
holder h {XYZ()};
cout<< \\\
** set_value()**<< endl
h.set_value(XYZ());
cout<< \\\
** set_value_by_const_ref()with nameless temporary **< endl
h.set_value_by_const_ref(XYZ());
cout<< \\\
** set_value()with named source **<< endl
h.set_value(xyz2);
cout<< \\\
** set_value_by_const_ref()with named source **< endl
h.set_value_by_const_ref(xyz2);
return 0;
}
预期输出:
**创建命名源供以后使用**
构造
**初始构造**
构造
move构造
** set_value()**
构造
移动分配
** set_value_by_const_ref()与无名称临时**
构造
分配
** set_value()与命名源**
构造的副本
移动分配
** set_value_by_const_ref()与命名源**
分配
注意副本/移动版本,但在调用 set_value_by_const_ref()
时使用无名临时的冗余副本分配。我注意到最终案例的表观效率增益。
我的命令行:
c ++ -o move -std = c ++ 1y -stdlib = libc ++ -O3 move.cpp
/ pre>
Assume I have the following class, which has a method
set_value
. Which implementation is better?class S { public: // a set_value method private: Some_type value; };
Pass by value, then move
void S::set_value(Some_type value) { this->value = std::move(value); }
Define two overloaded methods
void S::set_value(const Some_type& value) { this->value = value; } void S::set_value(Some_type&& value) { this->value = std::move(value); }
The first approach requires definition of one method only while the second requires two.
However, the first approach seems to be less efficient:
While for the second approach, only one assignment operation is performed.
So, which implementation is better? Or does it matter at all?
And one more question: Is the following code equivalent to the two overloaded methods in the second approach?
template <class T> void S::set_value(T&& value) { this->value = std::forward<T>(value); }
解决方案The compiler is free to elide (optimise away) the copy even if there would be side effects in doing so. As a result, passing by value and moving the result actually gives you all of the performance benefits of the two-method solution while giving you only one code path to maintain. You should absolutely prefer to pass by value.
here's an example to prove it:
#include <iostream> struct XYZ { XYZ() { std::cout << "constructed" << std::endl; } XYZ(const XYZ&) { std::cout << "copy constructed" << std::endl; } XYZ(XYZ&&) noexcept { try { std::cout << "move constructed" << std::endl; } catch(...) { } } XYZ& operator=(const XYZ&) { std::cout << "assigned" << std::endl; return *this; } XYZ& operator=(XYZ&&) { std::cout << "move-assigned" << std::endl; return *this; } }; struct holder { holder(XYZ xyz) : _xyz(std::move(xyz)) {} void set_value(XYZ xyz) { _xyz = std::move(xyz); } void set_value_by_const_ref(const XYZ& xyz) { _xyz = xyz; } XYZ _xyz; }; using namespace std; auto main() -> int { cout << "** create named source for later use **" << endl; XYZ xyz2{}; cout << "\n**initial construction**" << std::endl; holder h { XYZ() }; cout << "\n**set_value()**" << endl; h.set_value(XYZ()); cout << "\n**set_value_by_const_ref() with nameless temporary**" << endl; h.set_value_by_const_ref(XYZ()); cout << "\n**set_value() with named source**" << endl; h.set_value(xyz2); cout << "\n**set_value_by_const_ref() with named source**" << endl; h.set_value_by_const_ref(xyz2); return 0; }
expected output:
** create named source for later use ** constructed **initial construction** constructed move constructed **set_value()** constructed move-assigned **set_value_by_const_ref() with nameless temporary** constructed assigned **set_value() with named source** copy constructed move-assigned **set_value_by_const_ref() with named source** assigned
note the absence of any redundant copies in the copy/move versions but the redundant copy-assignment when calling
set_value_by_const_ref()
with nameless temporary. I note the apparent efficiency gain of the final case. I would argue that (a) it's a corner case in reality and (b) the optimiser can take care of it.my command line:
c++ -o move -std=c++1y -stdlib=libc++ -O3 move.cpp
这篇关于通过值和移动,或两种方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!