Может ли условный оператор привести к менее эффективному коду? - PullRequest
9 голосов
/ 05 августа 2011

Может ли ?: привести к менее эффективному коду по сравнению с if/else при возврате объекта?

Foo if_else()
{
    if (bla)
        return Foo();
    else
        return something_convertible_to_Foo;
}

Если bla имеет значение false, возвращаемый Foo напрямую создается из something_convertible_to_Foo.

Foo question_mark_colon()
{
    return (bla) ? Foo() : something_convertible_to_Foo;
}

Здесь тип выражения после return равен Foo, поэтому я предполагаю, что сначала создается некоторое временное Foo, если bla ложно для получения результатавыражение, а затем это временное должно быть сконструировано для копирования, чтобы вернуть результат функции.Это анализ звука?

Ответы [ 6 ]

8 голосов
/ 05 августа 2011

Временный Foo должен быть сконструирован в любом случае, и оба случая являются очевидным кандидатом на RVO, поэтому я не вижу никаких причин полагать, что компилятор не сможет выдать идентичный вывод в этом случае. Как всегда, на самом деле компиляция кода и просмотр выходных данных - лучший способ действий.

4 голосов
/ 05 августа 2011

Это наиболее определенно может быть, когда ссылки rvalue включены. Когда одна из двух ветвей является lvalue, а другая - rvalue, в зависимости от того, как вы идете, вы не получите правильную функцию, вызываемую хотя бы для одной из них. Когда вы выполняете оператор if способом, код будет вызывать правильный конструктор перемещения или копирования для возврата.

3 голосов
/ 05 августа 2011

Хотя я ценю вывод сборки, я все еще нахожу их «слишком» низкоуровневыми:)

Для следующего кода:

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);
}

Вот LLVM IR, сгенерированный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
}

Кстати, демоверсия llvm теперь использует Clang: p

Поскольку вопрос возникает не впервые, вв той или иной форме я хотел бы помнить, что, поскольку семантически обе формы эквивалентны, у хорошего компилятора нет оснований так или иначе относиться к ним с точки зрения оптимизации и генерации кода.Тернарный оператор - это просто синтаксический сахар.

2 голосов
/ 05 августа 2011

Как всегда в случае вопроса производительности: измерьте для рассматриваемого случая, есть слишком много вещей, чтобы принять во внимание, чтобы сделать любой прогноз.

Здесь я не удивлюсь, что у некоторых компиляторов возникают проблемы с одной или другой формой, в то время как другие быстро переходят к одному и тому же внутреннему представлению и, следовательно, генерируют точно такой же код.

0 голосов
/ 05 августа 2011

Я буду удивлен, если есть какая-либо разница, поскольку эти два логически эквивалентны. Но это будет зависеть от компилятора.

0 голосов
/ 05 августа 2011

Это зависит от компилятора. Насколько я знаю, на большинстве компиляторов он переведен на более чистый код 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 
    }
...