今天 在YT上看到c++weekly 提到一个避免使用std:move的案例
// Type your code here, or load an example.
#include <iostream>
#include <array>
class lifetime{
public:
lifetime(){std::cout<<"lifetime()//constructor"<<std::endl;}
lifetime(lifetime &a){std::cout<<"lifetime()//copy constructor"<<std::endl;}
lifetime(lifetime &&a){std::cout<<"lifetime()//move constructor"<<std::endl;}
~lifetime(){std::cout<<"~lifetime()//de-constructor"<<std::endl;}
lifetime &operator=(const lifetime &a) {
std::cout<<"copy assign"<<std::endl;
return *this;
}
lifetime &operator=(class lifetime &&a) {
std::cout<<"move assign"<<std::endl;
return *this;
}
};
void test_001 (){
std::cout<<"-----test 001---"<<std::endl;
lifetime a;
lifetime b ;
std::array<lifetime, 2> c = {a,b};
}
int main()
{
test_001();
return 0;
}
在这种情况 下你产生的对象创建是四次
-----test 001---
lifetime()//constructor
lifetime()//constructor
lifetime()//copy constructor
lifetime()//copy constructor
~lifetime()//de-constructor
~lifetime()//de-constructor
~lifetime()//de-constructor
~lifetime()//de-constructor
如果 生用std::move对上面的案例进行如下的修改
// Type your code here, or load an example.
#include <iostream>
#include <array>
class lifetime{
public:
lifetime(){std::cout<<"lifetime()//constructor"<<std::endl;}
lifetime(lifetime &a){std::cout<<"lifetime()//copy constructor"<<std::endl;}
lifetime(lifetime &&a){std::cout<<"lifetime()//move constructor"<<std::endl;}
~lifetime(){std::cout<<"~lifetime()//de-constructor"<<std::endl;}
lifetime &operator=(const lifetime &a) {
std::cout<<"copy assign"<<std::endl;
return *this;
}
lifetime &operator=(class lifetime &&a) {
std::cout<<"move assign"<<std::endl;
return *this;
}
};
void test_002 (){
std::cout<<"-----test 002---"<<std::endl;
lifetime a;
lifetime b ;
std::array<lifetime, 2> c = {std::move(a),std::move(b)};
}
int main()
{
test_002();
return 0;
}
你得到的结果 是什么样呢:
-----test 002---
lifetime()//constructor
lifetime()//constructor
lifetime()//move constructor
lifetime()//move constructor
~lifetime()//de-constructor
~lifetime()//de-constructor
~lifetime()//de-constructor
~lifetime()//de-constructor
可以看到 对象创建并没有减少
如果 采用lambda对这个例子人做如下修改:
// Type your code here, or load an example.
#include <iostream>
#include <array>
class lifetime{
public:
lifetime(){std::cout<<"lifetime()//constructor"<<std::endl;}
lifetime(lifetime &a){std::cout<<"lifetime()//copy constructor"<<std::endl;}
lifetime(lifetime &&a){std::cout<<"lifetime()//move constructor"<<std::endl;}
~lifetime(){std::cout<<"~lifetime()//de-constructor"<<std::endl;}
lifetime &operator=(const lifetime &a) {
std::cout<<"copy assign"<<std::endl;
return *this;
}
lifetime &operator=(class lifetime &&a) {
std::cout<<"move assign"<<std::endl;
return *this;
}
};
void test_003 (){
std::cout<<"-----test 003---"<<std::endl;
auto make_lifetime=[](){
lifetime l;
return l;
};
std::array<lifetime, 2> c = {make_lifetime(),make_lifetime()};
}
int main()
{
test_003();
return 0;
}
你会得到如下的神奇结果 :
-----test 003---
lifetime()//constructor
lifetime()//constructor
~lifetime()//de-constructor
~lifetime()//de-constructor
对象创建只有两个了
请问大神今们要如何解释这个操作呢。
lambda是如何把对像的创建给优化掉了呢
我在这里已经 把gcc 优化等级设定到了O0,
我想是编译器按什么样的规则给优化了。。有人知道 是什么规则吗。
还是编译 器有什么参数 可以开关这种优化。
在C++中,对于像 std::move 这样的函数,其主要目的是将一个左值转换为右值引用,从而触发移动语义而不是拷贝语义。在你的例子中,std::move(a) 和 std::move(b) 分别将 a 和 b 转换为右值引用,从而调用了移动构造函数。
然而,你观察到的现象可能与编译器的优化有关。编译器可能会对代码进行一些优化,尤其是在使用 lambda 表达式时,因为 lambda 表达式有时候可以被编译器内联,从而导致更多的优化机会。
在你的第三个例子中,使用了 lambda 表达式 make_lifetime,该 lambda 表达式返回一个局部对象 lifetime。编译器可能会注意到这个局部对象只在 lambda 表达式的作用域内使用,而不会被传递到其他地方。因此,编译器可能会优化掉对 lifetime 对象的实际创建,直接在数组初始化中构造数组元素。这是一种被称为返回值优化(Return Value Optimization,简称 RVO)或命名返回值优化(Named Return Value Optimization,简称 NRVO)的优化技术。
对于这种优化,编译器可以根据其实现的具体规则来确定是否执行。在你的例子中,使用了 lambda 表达式,这可能增加了编译器进行这种优化的机会。在实践中,编译器优化是一个复杂的主题,不同的编译器、版本和编译选项可能导致不同的行为。
如果你想深入了解编译器优化的细节,可以查看特定编译器的文档,以了解有关返回值优化、移动语义和其他相关优化的详细信息。
这是一个编译器的优化叫做copy Copy elision
可以生成一下的参数 关闭:-fno-elide-constructors
在C++里叫做RVO(return value optimization)
参考这篇文章
https://sigcpp.github.io/2020/06/08/return-value-optimization
https://en.cppreference.com/w/cpp/language/copy_elision