C ++:类/结构是否在编译时初始化?

时间:2012-05-24 04:56:55

标签: c++ class struct initialization c++03

假设我使用构造函数Foo编写了一个类Foo(int)。 我有这段代码:

Foo a(i), b = a + Foo(2);

如果我用代码调用代码中的构造函数,例如Foo(2),编译器是运行一次并将结果存储为运行时,还是在运行时执行?结构/类是否只包含POD数据类型是否相同?

假设它在运行时执行(我认为是这种情况),有没有办法让它在编译时运行,或者具有与它本身运行时相同的效果?

编辑:恐怕我没有说清楚。我指的是代码的Foo(2)部分,它是完全不可变的。此外,我无法使用C ++ 11(我正在使用GCC 4.1而无法升级),因此constexpr虽然有效,但不适合我。

6 个答案:

答案 0 :(得分:3)

有可能让a使用constant initialization,这是静态初始化,但要实现这一点:

  1. i大部分是常量表达
  2. Foo::Foo(int)必须为constexpr
  3. Foo:Foo(int)使用的任何/所有其他功能/ ctors也必须为constexpr
  4. 您的b - Foo(2)必须是constexprFoo::operator+(Foo const &)Foo operator+(Foo const &, Foo const &)(以您为准)必须是constexpr

    如果您想更详细地研究,常量表达式的定义在C ++ 11标准的第5.19节。我的直接猜测是,如果Foo相当简单,那么a可能就有可能,但我对b的确不太确定。

答案 1 :(得分:3)

  

假设我用构造函数Foo(int)编写了一个类Foo。我有这段代码:

Foo a(i), b = a + Foo(2);
     

如果我使用常量调用代码中的构造函数,编译器是否运行一次,并将结果存储为运行时,还是在运行时执行?

这有两个层次:

  • 是否有可能和合法的w.r.t.一个完美的优化者的标准 - 在编译时实现这一点,实现专家可以用无限的努力和天才做的一切 - 和
  • 是标准的必需的编译时行为 / 保证

i是否为编译时常量?如果没有,并且传递给i的{​​{1}}的值会影响其行为(无论是影响数据成员值还是影响日志记录等副作用),那么很明显,在编译时构造Foo(i)本身是不可能的。如果Foo::Foo(i)是常量,那么它本身可能仍然是不可能的 - 例如构造函数实现可能具有基于当前时间的行为,或者需要查阅其他一些运行时数据。这些问题也可能阻止i在编译时进行评估。

如果Foo(2)是常量,并且i的构造函数不依赖于其他仅运行时数据,则可能进行优化。但是,您的代码中没有任何内容要求C ++标准甚至尝试任何优化,更不用说能够优化它了。在Foo上调用的+运算符也是如此......优化可能是合法的,但肯定不是必需的。

实际上,我希望大多数当前的主流编译器能够针对编译时常量Foo优化Foo(i)的简单情况,但是要通过解决加法来挑战。如果您真的想知道,请在各种优化级别为您的编译器尝试....

  

假设它在运行时执行(我认为是这种情况),有没有办法让它在编译时运行,或者具有与它本身运行时相同的效果?

是的......你可能会从i中获得一些内容,这是一个用C ++ 11引入的关键字,它告诉编译器在编译时需要解析某些值(如果你{{1}对于编译器在编译时不需要支持的变量/值,它将报告错误。)

其次,通常可以使用模板表达编译时操作。为了让自己从这个方向开始,您可能希望搜索“C ++模板阶乘编译时”或类似内容,以了解如何编码基本计算。

答案 2 :(得分:2)

“as-if”规则适用,表示编译器可以执行任何喜欢的操作,前提是程序的可观察行为与标准中描述的相同。

如果:

  • Foo的构造函数在TU中可见,
  • 析构函数~Foo
  • 也是如此
  • 他们都没有任何副作用,
  • 他们的结果不依赖于任何需要在运行时解决的问题(比如时间,或者某些非const对象的值,我们知道这些对象的代码执行时可能已经修改过) ,
  • operator+是可见的,并且没有做任何事情让RHS的地址“逃脱”为未知代码,或者使用其地址进行可观察行为(例如打印出来)或做其他事情这要求对象实际存在,

然后一个足够聪明的优化器可以完全消除Foo(2)临时值,并且只要operator+使用RHS的数据成员,只需使用它知道这些成员将拥有的任何值。

或者,作为较小的优化,它可以将值放入程序的数据部分中Foo的实例的布局中,并将其用作Foo(2)。我想这就是为运行时存储结果的意思。

这种优化是否实际发生是完全特定于实现的,并且取决于您使用的编译器和标志。反汇编代码,看看到底发生了什么。

如果您执行以下操作,则可以确保{C} 03中只计算Foo(2)一次{/ 1}}:

static Foo foo2(2);
Foo a(i), b = a + foo2;

foo2是(根据标准)在运行时计算的,第一次执行代码。同样,编译器可以调用“as-if”规则在编译时执行部分或全部计算,但同​​样不需要这样做。

答案 3 :(得分:0)

Foo a(i), b = a + Foo(2);

此初始化在运行时发生,而不是在编译时发生。

编译时初始化只发生在内置类型中,如果它们的初始化程序可以在编译时计算,或者它们被声明为全局变量,或者static。在后两种情况下,它们在编译时是零初始化的。我在这里详细解释了这一点:

答案 4 :(得分:0)

可以在执行过程中的不同点调用已编译的代码。编译代码后,Foo(2)的值可能是不可变的。

答案 5 :(得分:-1)

这在运行时发生。如果您希望它在编译时发生,那么您需要对该值进行硬编码。