Будет ли хороший компилятор C ++ оптимизировать ссылки? - PullRequest
14 голосов
/ 26 января 2012

Я хочу написать шаблонную функцию, которая делает что-то с std::stack<T> и экземпляром T, например:

template<class StackType> inline
bool some_func( StackType const &s, typename StackType::value_type const &v ) {
  // ...
}

Причина, по которой я передаю v по ссылке, конечно жеоптимизировать для случая, когда StackType::value_type является struct или class и не копировать весь объект по значению.

Однако, если StackType::value_type является «простым» типом, таким как int, токонечно, лучше просто передать его по значению.

Вопрос в том, что для типа, такого как int, который станет int const& в качестве формального аргумента в вышеприведенной функции, компилятор оптимизирует удалениессылка и просто передать его по значению?

Ответы [ 4 ]

7 голосов
/ 28 января 2012

Я смотрю в опциях оптимизации gcc здесь http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

А на самом деле есть вариант для вашего случая:

-fipa-sra

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

Включено на уровнях -O2, -O3 и -Os

Покакак я знаю, -O2 - это обычная опция для сборки релизов на linux.

Итак, короткий ответ: один из хороших компиляторов делает

6 голосов
/ 26 января 2012

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

Для вызовов функций, которые не являются встроенными, ссылка, вероятно, будет реализована в виде указателя, а не передачи по значению.

6 голосов
/ 26 января 2012

Хотя я на самом деле не проверял никаких компиляторов для этого, я сомневаюсь в этом. Вы предполагаете, что передача константной ссылки неотличима от простой передачи значения, но это не так, и компилятор не должен предполагать, что это так.

Поскольку ссылка является константной, ваша функция не может изменить значение через нее, но другие части кода могут иметь доступ к исходной (неконстантной) переменной и могут изменять ее. Ваша функция может вызывать какую-то другую функцию, которая, например, изменяет ее, или одновременно может выполняться другой поток, выполняющий это.

Если что-то еще изменит эту исходную переменную, ваша функция с ее ссылкой должна увидеть новое значение. Если компилятор заменит ссылку на копию, функция все равно увидит старое значение.

Возможно, вас заинтересует библиотека вызовов Boost . Он предоставляет тип шаблона call_traits<T>::param_type, который является константной ссылкой для «больших» типов, которые вы не хотите копировать, и значение для «малых» типов, где копирование будет более эффективным. По сути, то, что вы хотите, чтобы компилятор делал неявно, вы можете делать явно с вашим кодом.

3 голосов
/ 26 января 2012

Это 64-битный компилятор MSVC2010, полная оптимизация:

int foo( int i )
{
 int a = i + i;

 return ( a );
}
//Disassembly:
//00000001`3ff11c50 8d0409          lea     eax,[rcx+rcx]
//00000001`3ff11c53 c3              ret

int bar( int const & i )
{
 int a = i + i;

 return ( a );
}
//Disassembly:
//00000001`3ff11c10 8b01            mov     eax,dword ptr [rcx]
//00000001`3ff11c12 03c0            add     eax,eax
//00000001`3ff11c14 c3              ret

В первом случае значение передается на RCX, во втором - адрес передается на RCX. Конечно, если функции встроены, как сказал Бен, то полученный код может быть абсолютно одинаковым в обоих случаях.

Передача экземпляров "маленьких" классов (скажем, класс имеет только int член данных и тривиальный конструктор, побитовый конструктор копирования и т. Д.) Выполняется быстрее по значению Компилятор оптимизирует его, по сути, просто передавая копию int. Таким образом, вы можете перегружать свою функцию, как указал Вайзард, используя некоторые черты типа. Примерно так:

template< typename PType >
PType Func( typename std::enable_if< ( sizeof( PType ) > sizeof( size_t ) ), PType const & >::type f )
{
 //large things: reference
}

template< typename PType >
PType Func( typename std::enable_if< ( sizeof( PType ) <= sizeof( size_t ) ), PType  >::type f )
{
 //small things: value
}
...