首页 新闻 会员 周边 捐助

关于lambad的神奇操作

0
[已解决问题] 解决于 2023-12-04 11:06

今天 在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++
会游泳的骆驼的主页 会游泳的骆驼 | 菜鸟二级 | 园豆:292
提问于:2023-11-30 18:17
< >
分享
最佳答案
1

在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 表达式,这可能增加了编译器进行这种优化的机会。在实践中,编译器优化是一个复杂的主题,不同的编译器、版本和编译选项可能导致不同的行为。

如果你想深入了解编译器优化的细节,可以查看特定编译器的文档,以了解有关返回值优化、移动语义和其他相关优化的详细信息。

奖励园豆:5
Technologyforgood | 大侠五级 |园豆:7688 | 2023-12-03 22:24
其他回答(1)
0

这是一个编译器的优化叫做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

会游泳的骆驼 | 园豆:292 (菜鸟二级) | 2023-12-01 15:38
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册