繁体   English   中英

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

[英]C++: Are classes/structs ever initialized at compile-time?

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

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

如果我在代码中使用常量(例如Foo(2)调用构造函数,则编译器是否会运行一次并存储结果供运行时使用,还是在运行时执行? struct / class是否仅包含POD数据类型是否相同?

假设它在运行时执行(我相信是这样),是否有办法使其在编译时运行,或者具有与运行时相同的效果?

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

这是可能有你的a使用constant initialization ,这是静态初始化,但要做到这一点:

  1. i最常表达
  2. Foo::Foo(int)必须是constexpr
  3. Foo:Foo(int)使用的所有其他/所有函数/变量也必须是constexpr

对于您的b来说,情况几乎相同Foo(2)必须为constexpr ,而Foo::operator+(Foo const &)Foo operator+(Foo const &, Foo const &) (无论您拥有什么)将必须是constexpr

如果要更详细地研究,常量表达式的定义在C ++ 11标准的第5.19节中。 我的直接猜测是,如果Foo非常简单,那么a可能是可能a ,但我对b的把握却很少。

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

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

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

这有两个层次:

  • 是否有可能通过法律为完善的优化程序制定标准-是否可以不费吹灰之力和天才地实现专家可以做的所有事情-在编译时做到这一点,以及
  • 是标准要求 / 保证的编译时行为。

i是编译时间常数吗? 如果不是,并且将i的值传递给Foo::Foo(i)影响其行为(是否影响数据成员值或诸如日志记录之类的副作用),那么显然在编译时构造Foo(i)本质上是不可能的。 如果i为常数,则从本质上来说仍然是不可能的-例如,构造函数的实现可能具有基于当前时间的行为,或者需要查询其他一些运行时数据。 这样的问题也可能阻止Foo(2)在编译时进行评估。

如果i是常量,并且Foo的构造函数不依赖于其他仅运行时的数据,则可以进行优化。 但是,您的代码中没有什么要求C ++标准甚至可以尝试任何优化,更不用说能够对其进行优化了。 Foo上调用+运算符的情况也是如此……优化可能是合法的,但当然不是必需的。

在实践中,我希望当前大多数主流编译器都可以针对编译时常数i优化Foo(i)简单情况,但是会遇到解决该问题的挑战。 如果您真的想知道,请在各种优化级别上为您的编译器尝试一下。

假设它在运行时执行(我相信是这样),是否有办法使其在编译时运行,或者具有与运行时相同的效果?

是的...您可能会从constexpr得到一些好处,这是C ++ 11引入的一个关键字,它告诉编译器在编译时需要解析某些值(如果您对变量/值使用constexpr ,则表明编译器不是需要在编译时提供支持,它将报告错误)。

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

适用“假设”规则,该规则表示只要程序的可观察行为与标准中描述的行为相同,编译器就可以执行其喜欢的任何事情。

如果:

  • Foo的构造函数在TU中可见,
  • 析构函数~Foo也是如此
  • 他们两个都没有任何副作用,
  • 它们的结果不依赖于运行时需要解决的任何问题(例如时间,或者众所周知,某些非常量对象的值可能已经在执行代码时被修改了),
  • operator+是可见的,并且不会做任何事情来使RHS的地址“转义”到未知代码中,或使用其地址进行可观察到的行为(例如将其打印出来),或执行任何其他需要对象实际存在的操作,

那么足够聪明的优化器可以完全消除Foo(2)临时性,并且无论operator+使用RHS的数据成员如何,只要使用它知道这些成员将拥有的任何值即可。

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

这些优化是否真正发生完全取决于实现,并且取决于您使用的编译器和标志。 分解代码以查看实际情况。

如果执行以下操作,则可以确保在C ++ 03中仅对Foo(2)进行一次计算:

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

foo2是(根据标准)在运行时(第一次执行代码)计算的。 同样,编译器可以在编译时调用“假设”规则来进行部分或全部计算,但这并不是必需的。

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

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

如果可以在编译时计算其初始化程序,或者将其声明为全局变量或static ,则编译时初始化仅适用于内置类型。 在后两种情况下,它们在编译时为零。 我在这里详细解释了这一点:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM