在代码优化过程中,C++11编译器是否会在可能的情况下将局部变量转换为右值
Do C++11 compilers turn local variables into rvalues when they can during code optimization?
例如,有时将复杂或长的表达式拆分为多个步骤是明智的(第二个版本并不更清楚,但它只是一个示例):
return object1(object2(object3(x)));
可以写成:
object3 a(x);
object2 b(a);
object1 c(b);
return c;
假设所有3个类都实现了以右值为参数的构造函数,那么第一个版本可能会更快,因为临时对象是传递的,并且可以移动。我假设在第二个版本中,局部变量被认为是左值。但是,如果以后不使用这些变量,C++11编译器是否会优化代码,使变量被视为右值,并且两个版本的工作方式完全相同?我最感兴趣的是Visual Studio 2013的C++编译器,但我也很高兴知道GCC编译器在这方面的表现。
谢谢,Michal
在这种情况下,编译器不能破坏"好像"规则。但是你可以使用std::move
来达到想要的效果:
object3 a(x);
object2 b(std::move(a));
object1 c(std::move(b));
return c;
正如juancapanza所说,编译器不能(在C++级别)违反"好像"规则;也就是说,所有的转换都应该生成语义等效的代码。
然而,在C++级别之外,当代码被优化时,可能会出现更多的机会。
因此,这实际上取决于对象本身:如果移动构造函数/析构函数有副作用,而(去)分配内存是副作用,那么优化就无法进行。如果只使用带有默认移动构造函数/析构函数的POD,那么它可能会自动优化。
但是如果以后不使用这些变量,C++11编译器会优化吗代码,因此变量被认为是右值,并且两者都是版本工作完全相同?
这是可能的,但很大程度上取决于您的类型考虑以下POD类型为point
:的示例
#include <cstdio>
struct point {
int x;
int y;
};
static point translate(point p, int dx, int dy) {
return { p.x + dx, p.y + dy };
}
static point mirror(point p) {
return { -p.x, -p.y };
}
static point make_point(int x, int y) {
return { x, y };
}
int main() {
point a = make_point(1, 2);
point b = translate(a, 3, 3);
point c = mirror(b);
std::printf("(x,y) = (%d,%d)n", c.x, c.y);
}
我看了汇编代码,以下是整个程序(!)基本上编译成的内容(因此下面的代码是生成的汇编代码的C近似值):
int main() {
std::printf("(x,y) = (-4,-5)n");
}
它不仅去掉了所有的局部变量,还在编译时进行计算我试过gcc和clang,但没有试过msvc。
好吧,让我们把程序变得更复杂一点,这样它就不能进行计算:
int main(int argc, char* argv[]) {
int x = *argv[1]-'0';
int y = *argv[2]-'0';
point a = make_point(x,y);
point b = translate(a, 3, 3);
point c = mirror(b);
std::printf("(x,y) = (%d,%d)n", c.x, c.y);
}
要运行此代码,您必须像./a.out 1 2
一样调用它。
整个程序在优化后简化为这个程序(用C重写程序集):
int main(int argc, char* argv[]) {
int x = *argv[1]-'0';
int y = *argv[2]-'0';
std::printf("(x,y) = (%d,%d)n", -(x+3), -(y+3));
}
因此,它去掉了a, b, c
和所有函数make_point()
、translate()
和mirror()
,并在编译时尽可能多地进行计算
由于Matthieu M.的回答中提到的原因,不要期望对更复杂的类型(尤其是非POD)进行如此好的优化。
根据我的经验,内联是至关重要的。努力工作,以便可以轻松地内联函数。使用链接时间优化。
请注意,除了可以极大地加快代码速度的移动语义外,编译器还可以进行(N)RVO-(命名)返回值优化,这实际上可以提高代码的效率。我已经测试了你的例子,在g++4.8中,你的第二个例子实际上可能更优化:
object3 a(x);
object2 b(a);
object1 c(b);
return c;
从我的实验来看,它会调用构造函数/析构函数8次(1 ctr+2个复制ctr+1个移动ctr+4个dtrs),而其他方法会调用它10次(1个ctr+5个移动ctrs)。但正如user2079303所评论的,move构造函数仍然应该优于copy构造函数,而且在本例中,所有调用都将内联,因此不会发生函数调用开销。
复制/移动省略实际上是"好像"规则的一个例外,这意味着有时你可能会对你的构造函数/析构函数(即使有副作用)没有被调用感到惊讶。
http://coliru.stacked-crooked.com/a/1ca7ebec0567e48f
(您可以使用-fno elide构造函数参数禁用(N)RVO)
#include <iostream>
#include <memory>
template<int S>
struct A {
A() { std::cout<<"A::A"<<std::endl; }
template<int S2>
A(const A<S2>&) { std::cout<<"A::A&"<<std::endl; }
template<int S2>
A(const A<S2>&&) { std::cout<<"A::A&&"<<std::endl; }
~A() { std::cout<<"~A::A"<<std::endl;}
};
A<0> foo () {
A<2> a; A<1> b(a); A<0> c(b); return c; // calls dtor/ctor 8 times
//return A<0>(A<1>(A<2>())); // calls dtor/ctor 10 times
}
int main()
{
A<0> a=foo();
return 0;
}
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 为什么默认情况下初始化局部变量
- 递归计算在不使用静态局部变量,全局变量或静态函数的情况下,在递归函数中发生的环数
- 在某些情况下,如何理解允许实现将非局部变量的动态初始化视为静态初始化
- 在溢出的情况下,变量的值始终为负数
- 在代码优化过程中,C++11编译器是否会在可能的情况下将局部变量转换为右值
- C++-在不知道变量的名称的情况下使用变量
- 理解在c++中的真实情况下类变量是如何存储在内存中的
- gdb在选项卡完成的情况下打印变量时冻结
- 为什么编译器不允许我在开关情况下使用变量?
- 如何在不知道特定类型的情况下引用变量
- 有没有任何情况下,变量可以被赋予一个void函数
- 默认情况下全局变量是 extern 还是等效于在全局中使用 extern 声明变量
- 初始化各种开关情况下的变量
- 如何在不使用赋值运算符的情况下为变量赋值
- 我可以在不产生引用存储成本的情况下对变量进行别名吗
- 是否有可能在不使用指针的情况下将变量的作用域扩展到if语句之外?
- 为什么c++程序要为局部变量分配比在最坏情况下需要的更多的内存?
- 在事先不知道总大小的情况下写入变量
- 在未初始化的情况下使用C++变量