为什么三元运算符会阻止MSVC中的返回值优化(RVO)?请考虑以下完整的示例程序:
#include <iostream>
struct Example
{
Example(int) {}
Example(Example const &) { std::cout << "copy\n"; }
};
Example FunctionUsingIf(int i)
{
if (i == 1)
return Example(1);
else
return Example(2);
}
Example FunctionUsingTernaryOperator(int i)
{
return (i == 1) ? Example(1) : Example(2);
}
int main()
{
std::cout << "using if:\n";
Example obj1 = FunctionUsingIf(0);
std::cout << "using ternary operator:\n";
Example obj2 = FunctionUsingTernaryOperator(0);
}
使用VC 2013编译:{{1}}
输出:
cl /nologo /EHsc /Za /W4 /O2 stackoverflow.cpp
显然,三元运算符以某种方式阻止了RVO。为什么?为什么编译器不够聪明,看不到使用三元运算符的函数与使用if语句的函数做同样的事情,并相应地进行优化?
答案 0 :(得分:4)
看看程序输出,在我看来,确实,编译器在这两种情况下都没有,为什么?
因为,如果没有激活elide,正确的输出将是:
所以,我希望,至少2&#34; copy&#34;在我的屏幕输出。实际上,如果我执行你的程序,使用g ++编译,使用-fno-elide-constructor,我从每个函数中获得了2条复制消息。
有趣的是,如果我对clang做同样的事情,我得到3&#34;复制&#34;调用函数FunctionUsingTernaryOperator(0);
时的消息,我想,这是由于编译器如何实现三元组。我想它正在生成一个临时解决三元运算符并将此临时值复制到return语句。
答案 1 :(得分:2)
此related question包含答案。
标准说明在返回声明中允许复制或移动省略时:(12.8.31)
因此,基本上只有在以下情况下才会出现复制省略:
如果您的表达式不是命名对象或临时对象,则可以回退复制。
一些有趣的行为:
return (name);
不会阻止复制省略(请参阅this question)return true?name:name;
应该防止复制,但gcc 4.6至少在这个问题上是错误的(参见this question)修改强>
我在上面留下了原来的答案,但Christian Hackl的评论是正确的,它没有回答这个问题。
就规则而言,示例中的三元运算符会产生一个临时对象,因此12.8.31允许复制/移动。所以从C ++语言的角度来看。 从FunctionUsingTernaryOperator返回时,完全允许编译器忽略副本。
现在很明显,没有完成任务。我想唯一的原因是Visual Studio Compiler团队还没有实现它。而且因为从理论上讲,他们可以在将来的版本中使用它们。
答案 2 :(得分:0)
我看到它违反了有关RVO的一条一般规则-(应该)在单个位置定义返回对象。
下面的代码片段满足规则:
e
但是在如下所示的原始表达式中,根据MSVC在两个不同的位置定义了两个Example e;
e = (i == 1)? Example{1} : Example{2};
return e;
对象:
Example
尽管两个片段之间的转换对于人类来说是微不足道的,但我可以想象,如果没有专门的实现,它不会自动在编译器中发生。换句话说,这是一个极端案例,从技术上讲可以RVO,但是开发人员没有意识到。