Can the conditional operator lead to less efficient code?
Can ?:
lead to less efficient code compared to if/else
when returning an object?
Foo if_else()
{
if (bla)开发者_StackOverflow社区
return Foo();
else
return something_convertible_to_Foo;
}
If bla
is false, the returned Foo
is directly constructed from something_convertible_to_Foo
.
Foo question_mark_colon()
{
return (bla) ? Foo() : something_convertible_to_Foo;
}
Here, the type of the expression after the return
is Foo
, so I guess first some temporary Foo
is created if bla
is false to yield the result of the expression, and then that temporary has to be copy-constructed to return the result of the function. Is that analysis sound?
A temporary Foo
has to be constructed either way, and both cases are a clear candidate for RVO, so I don't see any reason to believe the compiler would fail to produce identical output in this case. As always, actually compiling the code and looking at the output is the best course of action.
It most definitely can where rvalue references are enabled. When one of the two branches is an lvalue and the other an rvalue, whichever way you go, you're going to not get the correct function called for at least one of them. When you do the if statement way, then the code will call the correct move or copy constructor for the return.
While I appreciate assembly output, I still find them a bit "too" low-level :)
For the following code:
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);
}
Here is the LLVM IR generated by Clang
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
}
By the way, the llvm try out demo now uses Clang :p
Since it is not the first time that the question comes up, in one form or another, I would like to remember that since semantically both forms are equivalent, there is no reason for a good compiler to treat them any differently as far as optimization and code generation are concerned. The ternary operator is just syntactic sugar.
As always in case of performance question: measure for the case at hand, there are too many things to take into account to do any prediction.
Here, I'd not be surprised that some compilers have problems with one form or the other while others get rapidly to the same internal representation and thus generate exactly the same code.
I will be surprised if there is any difference since the two are logically equivalent. But this will depend on the compiler.
It depends on compiler. As far as i know, on most of compilers, if-else it's translated to cleaner ASM code and it's faster.
Edit: Assuming the code below
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;
}
will be translated in ASM like this
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
}
精彩评论