Учтите следующее:
inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;}
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;}
Синтаксис f2
является более компактным, но сделать стандарт гарантирует, что f1
а также f2
строго эквивалентны?
Кроме того, если я хочу, чтобы компилятор оптимизировал это выражение, если b
а также i
известны во время компиляции, какую версию мне выбрать?
Ну да, оба эквивалентны. bool
является интегральным типом и true
гарантированно конвертировать в 1
в целочисленном контексте, в то время как false
гарантированно конвертировать в 0
,
(Обратное также верно, то есть ненулевые целочисленные значения гарантированно преобразуются в true
в логическом контексте, в то время как нулевые целые значения гарантированно преобразуются в false
в логическом контексте.)
Поскольку вы работаете с неподписанными типами, можно легко придумать другие, возможно, основанные на битах, но совершенно портативные реализации того же самого, например
i & -(unsigned) b
хотя достойный компилятор должен иметь возможность выбрать лучшую реализацию для любой из ваших версий.
Постскриптум К моему большому удивлению, GCC 4.1.2 скомпилировал все три варианта буквально буквально, то есть он использовал инструкцию машинного умножения в варианте на основе умножения. Это было достаточно умно, чтобы использовать cmovne
инструкция по ?:
вариант, чтобы сделать его без ветвления, что вполне возможно сделало его наиболее эффективной реализацией.
Да. Можно предположить true
является 1
а также false
является 0
при использовании в выражениях, как вы и гарантировано:
C ++ 11, Integral Promotions, 4.5:
Значение типа bool можно преобразовать в значение типа int с помощью
ложь становится нулем, а правда становится единым.
Компилятор будет использовать неявное преобразование, чтобы сделать unsigned int
от b
Так что да, это должно работать. Вы пропускаете проверку условий простым умножением. Какой из них более эффективен / быстрее? Не знаю Хороший компилятор, скорее всего, оптимизировал бы обе версии.
FWIW, следующий код
inline unsigned int f1(const unsigned int i, const bool b) {return b ? i : 0;}
inline unsigned int f2(const unsigned int i, const bool b) {return b*i;}
int main()
{
volatile unsigned int i = f1(42, true);
volatile unsigned int j = f2(42, true);
}
скомпилированный с gcc -O2 производит эту сборку:
.file "test.cpp".def ___main; .scl 2; .type 32; .endef
.section .text.startup,"x".p2align 2,,3
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB2:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
call ___main
movl $42, 8(%esp) // i
movl $42, 12(%esp) // j
xorl %eax, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE2:
Там не так много осталось ни f1
или же f2
, как вы видете.
Что касается стандарта C ++, то компилятору разрешено делать все, что касается оптимизации, если он не меняет наблюдаемого поведения ( как будто правило).