C++ 条件运算符是否会导致效率较低的代码?
与返回对象时的C++ 条件运算符是否会导致效率较低的代码?,c++,constructor,return-value,ternary-operator,conditional-operator,C++,Constructor,Return Value,Ternary Operator,Conditional Operator,与返回对象时的if/else相比,?:是否会导致代码效率降低 Foo if_else() { if (bla) return Foo(); else return something_convertible_to_Foo; } 如果bla为false,则返回的Foo将直接从可转换的东西\u转换为\u Foo Foo question_mark_colon() { return (bla) ? Foo() : something_conve
if/else
相比,?:
是否会导致代码效率降低
Foo if_else()
{
if (bla)
return Foo();
else
return something_convertible_to_Foo;
}
如果bla
为false,则返回的Foo
将直接从可转换的东西\u转换为\u Foo
Foo question_mark_colon()
{
return (bla) ? Foo() : something_convertible_to_Foo;
}
在这里,
return
后面的表达式类型是Foo
,因此我猜首先,如果bla
为false,则会创建一些临时的Foo
,以生成表达式的结果,然后必须复制构造该临时表达式以返回函数的结果。这是正确的分析吗?这取决于编译器。据我所知,在大多数编译器上,如果不是这样的话,它会被转换成更干净的ASM代码,而且速度更快
编辑:假设代码如下
int a = 10;
int b = 20;
int c = 30;
int d = 30;
int y = 30;
y = (a > b) ? c : d;
if (a > b)
{
y = c;
}
else
{
y = d;
}
将被翻译成ASM,如下所示
y = (a > b) ? c : d;
008C13B1 mov eax,dword ptr [a]
008C13B4 cmp eax,dword ptr [b]
008C13B7 jle wmain+54h (8C13C4h)
008C13B9 mov ecx,dword ptr [c]
008C13BC mov dword ptr [ebp-100h],ecx
008C13C2 jmp wmain+5Dh (8C13CDh)
008C13C4 mov edx,dword ptr [d]
008C13C7 mov dword ptr [ebp-100h],edx
008C13CD mov eax,dword ptr [ebp-100h]
008C13D3 mov dword ptr [y],eax
if (a > b)
008C13D6 mov eax,dword ptr [a]
008C13D9 cmp eax,dword ptr [b]
008C13DC jle wmain+76h (8C13E6h)
{
y = c;
008C13DE mov eax,dword ptr [c]
008C13E1 mov dword ptr [y],eax
}
else
008C13E4 jmp wmain+7Ch (8C13ECh)
{
y = d;
008C13E6 mov eax,dword ptr [d]
008C13E9 mov dword ptr [y],eax
}
临时的
Foo
必须以任何一种方式构造,这两种情况都是RVO的明显候选,因此我认为没有任何理由相信编译器在这种情况下无法生成相同的输出。和往常一样,编译代码并查看输出是最好的做法。如果有任何区别,我会感到惊讶,因为两者在逻辑上是等价的。但这将取决于编译器。在启用右值引用的情况下,它肯定可以。当两个分支中的一个是左值,另一个是右值时,无论您选择哪种方式,都不会为其中至少一个分支调用正确的函数。当您以if语句的方式执行时,代码将调用正确的move或copy构造函数进行返回。在遇到性能问题时总是这样:对于手头的情况,要进行任何预测,需要考虑的事情太多了
在这里,有些编译器在一种或另一种形式上存在问题,而其他编译器则快速使用相同的内部表示,从而生成完全相同的代码,对此我并不感到惊讶。虽然我欣赏汇编输出,但我仍然发现它们的级别有点“太”低:) 对于以下代码:
struct Foo { Foo(): i(0) {} Foo(int i): i(i) {} int i; };
struct Bar { Bar(double d): d(d) {} double d; operator Foo() const { return Foo(d); } };
Foo If(bool cond) {
if (cond) { return Foo(); }
return Bar(3);
}
Foo Ternary(bool cond) {
return cond ? Foo() : Bar(3);
}
下面是由Clang生成的LLVM IR
define i64 @If(bool)(i1 zeroext %cond) nounwind readnone {
entry:
%retval.0.0 = select i1 %cond, i64 0, i64 3 ; <i64> [#uses=1]
ret i64 %retval.0.0
}
define i64 @Ternary(bool)(i1 zeroext %cond) nounwind readnone {
entry:
%tmp.016.0 = select i1 %cond, i64 0, i64 3 ; <i64> [#uses=1]
ret i64 %tmp.016.0
}
define i64@If(bool)(i1 zeroext%cond)nounwind readnone{
条目:
%retval.0.0=选择i1%cond、i64 0、i64 3;[#使用=1]
ret i64%retval.0.0
}
定义i64@三元(bool)(i1 zeroext%cond)无{
条目:
%tmp.016.0=选择i1%cond、i64 0、i64 3;[#使用=1]
ret i64%tmp.016.0
}
顺便说一下,现在使用的是叮当声:p
由于这不是第一次以这样或那样的形式出现这个问题,我想记住,由于两种形式在语义上都是等价的,所以就优化和代码生成而言,一个好的编译器没有理由对它们进行任何不同的处理。三元运算符只是语法上的糖。我认为第一个示例还必须创建一个临时对象?将这两个对象编译到汇编程序中,自己看看。哦,我认为返回对象的语义应该是直接初始化,但它似乎是复制初始化。不像DeadMG指出的那样,如果对象具有移动语义,则这些方法中只有一个会生成临时对象。@Benj:它们都会生成临时对象,但在这种情况下,编译器可以对其进行优化。鉴于DeadMG所建议的情况,标准可能不允许在三元运算符情况下省略所有可能的拷贝/移动,但我不确定。也可能是编译器还没有执行优化;移动构造函数毕竟是一个相对较新的功能。是的,很抱歉,它们都会生成一个临时的,我应该说只有在三元情况下,临时的才会是复制构造的结果,就像你说的,我不确定这是编译器特定的还是标准的。实际上,当我运行这个时,三元方法同时调用了copy和move构造函数……为什么您认为在三元运算符的情况下不会调用move构造函数?我不明白为什么它不会…嘿,你说得对!我刚刚在调试器中尝试了这个。当if/else只调用移动构造函数时,三元运算符会导致调用复制构造函数和移动构造函数。这是为什么?@Benj:因为运算符只能有一个值-左值或右值。这意味着当您有一个右值和一个左值时,您将丢失左值/右值数据。