假设我有以下内容:
int main() {
SomeClass();
return 0;
}
如果没有优化,将调用SomeClass()构造函数,然后调用它的析构函数,并且对象将不再存在。
但是,根据IRC通道,如果编译器认为对SomeClass构造函数/析构函数没有副作用,那么构造函数/析构函数调用可能会被优化掉。
我认为显而易见的方法是不使用一些构造函数/析构函数(例如使用函数或静态方法等),但有没有办法确保调用构造函数/析构函数?
答案 0 :(得分:7)
但是,根据IRC频道,如果编译器认为对SomeClass构造函数/析构函数没有副作用,那么构造函数/析构函数调用可能会被优化掉。
粗体部分错误。应该是:知道 没有可观察行为
E.g。来自最新标准的§1.9(有更多相关的引用):
执行格式良好的程序的符合实现应产生相同的可观察行为 作为具有相同程序的抽象机的相应实例的可能执行之一 和相同的输入。但是,如果任何此类执行包含未定义的操作,则此国际 标准不要求使用该输入执行该程序的实现(甚至不是 关于第一次未定义操作之前的操作。)
事实上,这整个机制支撑着最普遍存在的C ++语言习语: Resource Acquisition Is Initialization
<强> 新闻背景 强>
让编译器优化掉琐碎的case-constructors 非常有帮助。它允许迭代器编译为与使用原始指针/索引器完全相同的性能代码。
它还允许函数对象编译为与内联函数体完全相同的代码。
这使得C ++ 11 lambdas 完美最适合简单的用例:
factorial = std::accumulate(begin, end, [] (int a,int b) { return a*b; });
lambda编译为类似于
的仿函数对象struct lambda_1
{
int operator()(int a, int b) const
{ return a*b; }
};
编译器看到构造函数/析构函数可以被省略并且函数体被内联。最终结果是最优 1
该标准包含一个非常有趣的例子,相反,激发你的想象力。
§20.7.2.2.3
[ Note:
临时对象构造和销毁导致的使用计数更新不是 可观察到的副作用,因此实施可能会影响效果(和隐含的保证) 不同的手段,没有创造一个临时的。特别是在示例中:shared_ptr<int> p(new int); shared_ptr<void> q(p); p = p; q = p;
这两项任务可能都是无操作。
—end note ]
IOW:不要低估优化编译器的能力。这绝不意味着语言保证会被抛到窗外!
1 虽然可以有更快的算法来获得阶乘,但取决于问题域:)
答案 1 :(得分:0)
我确定'SomeClass :: SomeClass()'没有实现为'inline',编译器无法知道构造函数/析构函数没有副作用,它总是会调用构造函数/析构函数。
答案 2 :(得分:0)
如果编译器正在优化构造函数/析构函数调用的可见效果,那么它就是错误的。如果它没有明显效果,那么你无论如何都不应该注意到它。
但是,让我们假设你的构造函数或析构函数确实具有可见效果(因此构造和随后对该对象的破坏实际上不是无操作),编译器可以合理地认为它不会(并不是说我能想到这种情况,但那时,我可能只是缺乏想象力)。然后,以下任何策略都应该有效:
确保编译器无法查看构造函数和/或析构函数的定义。如果编译器不知道构造函数/析构函数的作用,则不能认为它没有效果。但请注意,这也会禁用内联。如果您的编译器不进行跨模块优化,只需将构造函数/析构函数放入另一个文件就足够了。
确保您的构造函数/析构函数确实具有可观察的行为,例如通过使用volatile变量(每次读或写一个volatile变量都被认为是C ++中的可观察行为)。
然而,让我再次强调,你不可能做任何事情,除非你的编译器是非常错误的(在这种情况下我强烈建议你改变编译器: - ))。