C++构造函数成员赋值优化

3
我想知道在这种情况下C++编译器是否能够进行优化。假设我们有一个类如下所示:
class Foo {
public:
  Foo() : a(10), b(11), c(12) {};
  int a;
  int b;
  int c;
};

我使用这个类的方式如下:

int main(int argc, char *argv[]) {
  Foo f;
  f.a = 50;
  f.b = 51;
  f.c = 52;
  return 0;
}

编译器会生成代码来设置a、b和c的默认值分别为10、11和12,然后将它们设置为50、51和52吗?或者它可以延迟分配这些初始值,并仅在以后分配值(50、51、52),因为没有读取操作?基本上,它是否必须生成代码来写入这六个值,还是可以优化为三个?
如果是这样,复杂类型(结构体、类)是否也适用?这叫什么名字,我可以在哪里阅读更多相关信息?
如果不是,为什么不是?

1
你可以移除构造函数,提供默认成员初始化器,这样你的类就会变成一个聚合体。然后你就可以进行聚合初始化而不用担心它了。 - StoryTeller - Unslander Monica
很不幸,我无法控制相关的类 - 不过还是谢谢你的提示! - mach990
我认为,除非编译器能够检查每个实例化是否立即丢弃其值,否则它必须按照给定的构造函数进行操作。 - SoronelHaetir
关于你的问题,由于构造函数的定义是内联提供的,我认为现代编译器很可能会进行你想要的优化。根据as-if规则,这是完全有效的。但是,如果不查看生成的汇编代码,就无法确定。 - StoryTeller - Unslander Monica
我很好奇,于是查了一下。这里是GCC和Clang的输出。GCC从-O1开始就进行了激进的优化,而Clang则从-O2开始。 - StoryTeller - Unslander Monica
@StoryTeller非常感谢您!那个网站太棒了,我之前从没见过。 - mach990
3个回答

2

这显然取决于编译器 - 但肯定有至少一些编译器可以并且会消除死存储。事实上,根据您使用结果的方式,编译器可能会消除所有存储,无论是死存储还是其他存储。

例如,如果我们按照当前代码完全编译,我们最终得到的汇编语言就像这样:

xor eax, eax
ret

这就是了——因为您从未使用存储的任何值,它完全消除了处理这些值的所有代码。剩下的只是main返回0的事实,因此它只会生成让main返回零的代码。

但这可能不是你非常关心的情况,所以让我们扩展一下代码,展示更接近你关心的内容。

#include <iostream>

class Foo {
public:
  Foo() : a(10), b(11), c(12) {};
  int a;
  int b;
  int c;

  friend std::ostream &operator<<(std::ostream &os, Foo const &f) {
      return os << "(" << f.a << ", " << f.b << ", " << f.c << ")";
  }
};

int main() {
  Foo f;
  f.a = 50;
  f.b = 51;
  f.c = 52;

  std::cout << f << "\n";
}

在这种情况下,编译器仍会消除所有相关的存储,并生成代码以直接写出我们在源代码中给定的字面值。
mov esi, 50
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

[并且对于51和52也是同样的序列重复]
参考资料: Godbolt

1

是的,编译器可以进行优化以删除初始化。这种技术称为死代码消除优化。确定是否发生了死代码消除是数据流分析的一部分。


-2

它将首先将三个值分配给a、b和c,然后在此之后将它们更改为后面分配的值。

这是因为在C++中,执行是逐行进行的。所以首先对象被声明并且在声明时通过构造函数分配了值。之后a、b和c的值被更改。

为了优化它,您可以使用带有默认参数的构造函数,如下所示:

class Foo{
public:
int a, b, c;
Foo(x = 10, y = 11, z = 12){
   a = x;
   b = y;
   c = z;
}
};

int main(){
Foo f; //a gets 10, b gets 11, c gets 12
Foo fi(51, 52, 53); //a gets 51, b gets 52, c gets 53
}

1
这是因为在C++中,执行是逐行进行的。这实际上意味着“编译器可以自由优化,只要生成的输出在任何输入情况下与按行执行C++所产生的结果相同。”在OP的情况下,优化器可以生成“int main(){}”。 - Bathsheba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接