所以我有一个问题要问你。 :) 你能告诉我以下代码应该产生的输出吗?
#include <iostream>
struct Optimized
{
Optimized() { std::cout << "ctor" << std::endl; }
~Optimized() { std::cout << "dtor" << std::endl; }
Optimized(const Optimized& copy) { std::cout << "copy ctor" << std::endl; }
Optimized(Optimized&& move) { std::cout << "move ctor" << std::endl; }
const Optimized& operator=(const Optimized& rhs) { std::cout << "assignment operator" << std::endl; return *this; }
Optimized& operator=(Optimized&& lhs) { std::cout << "move assignment operator" << std::endl; return *this; }
};
Optimized TestFunction()
{
Optimized a;
Optimized b = a;
return b;
}
int main(int argc, char* argv[])
{
Optimized test = TestFunction();
return 0;
}
我的第一反应是:
并且它是真的,但只有在关闭编译器优化时才 。当优化打开时,输出完全不同。打开优化后,输出为:
使用编译器优化,测试变量是返回变量。
我的问题是,什么条件会导致不以这种方式优化?
我一直被告知,返回一个导致额外复制构造函数的结构/类可以通过作为引用传入进行优化,但编译器正在为我做这些。那么返回一个仍被视为不良形式的结构?
答案 0 :(得分:14)
这称为复制Elision ,是一种特殊处理,而不是复制/移动。
标准特别允许优化,只要可以复制/移动(即,声明并可访问该方法)。
编译器中的实现通常称为返回值优化。有两种变化:
return "aa" + someString;
)两者都是由主要编译器实现的,但后者可能只会在更高的优化级别上启动,因为它更难以检测。
因此,要回答关于返回结构的问题:我会推荐它。考虑:
// Bad
Foo foo;
bar(foo);
-- foo can be modified here
// Good
Foo const foo = bar();
后者不仅更清晰,而且还允许const
强制执行!
答案 1 :(得分:4)
两种输出都是允许的。 C ++ 03语言标准在第12.8 / 15节中说:
当满足某些条件时,允许实现省略类对象的复制结构, 即使对象的复制构造函数和/或析构函数有副作用。在这种情况下,实施 将省略的复制操作的源和目标视为两种不同的引用方式 同一个对象,并且该对象的破坏发生在两个对象的后期 如果没有优化,就会被破坏。 111)这种复制操作的省略是允许的 以下情况(可以合并以消除多个副本):
- 在具有类返回类型的函数中的
中,可以省略操作return
语句中,当表达式是a的名称时 非易失性自动对象具有与函数返回类型相同的cv-nonqualified类型的副本 通过将自动对象直接构造到函数的返回值- 当未绑定到引用(12.2)的临时类对象将被复制到类时 对于具有相同cv-unqualified类型的对象,可以通过构造临时来省略复制操作 对象直接进入省略副本的目标
答案 2 :(得分:2)
此代码将产生的输出是不可预测的,因为语言规范明确允许对类对象的“不必要的”临时副本进行可选的消除(省略),即使它们的副本构造函数具有副作用。
是否会发生这种情况可能取决于可能因素,包括编译器优化设置。
在我看来,调用上述复制省略“优化”并不完全正确(虽然在这里使用这个术语的愿望是完全可以理解的,并且它被广泛用于此目的)。我会说优化一词应保留在编译器偏离抽象C ++机器行为的情况下,同时保留可观察量该计划的行为。换句话说,真正的优化意味着违反语言规范的抽象要求。由于在这种情况下没有违规(标准明确允许复制省略),因此没有真正的“优化”。我们在这里观察的是C ++语言在抽象层面上的工作原理。根本不需要涉及“优化”的概念。
答案 3 :(得分:1)
即使按值传回,编译器也可以使用返回值优化来优化额外的副本。 http://en.wikipedia.org/wiki/Return_value_optimization