14

Посмотрите на этот гипотетический файл заголовка:Могу ли я позволить компилятору C++ решить, следует ли передавать по значению или передавать по ссылке?

template <class T> 
class HungryHippo { 
public: 
    void ingest(const T& object); 
private: 
    ... 
} 

Теперь для HungryHippo<string> это имеет смысл, что вы хотели бы ingest ссылки на струнах - копирование строки может быть очень дорого! Но для HungryHippo<int> это делает путь менее ощутимым. Передача int напрямую может быть действительно дешевой (большинство компиляторов сделает это в регистре), но передача ссылки на int - лишний ненужный уровень косвенности. Все это относится и к возвращающим значениям.

Есть ли способ предложить компилятору «эй, я не собираюсь изменять аргумент, так что вы решаете, следует ли передавать по значению или по ссылке, в зависимости от того, что, по вашему мнению, лучше»?

некоторые вещи, которые могут иметь отношение:

  • Я может фальсифицировать этот эффект вручную написания template <class T, bool PassByValue> class HungryHippo и затем, специализирующихся на PassByValue. Если бы я хотел получить действительно фантазию, я мог бы даже выслать PassByValue на основе sizeof(T) и std::is_trivially_copyable<T>. В любом случае, это большая часть дополнительной работы, когда реализации будут выглядеть примерно одинаково, и я подозреваю, что компилятор может намного лучше решить, передавать ли значение по значению, чем я могу.
  • Проект libc++, похоже, решает эту проблему, встраивая множество функций, поэтому компилятор может сделать выбор на один уровень выше, но в этом случае реализация ingest довольно сложна и не стоит встраивать. Как поясняется в комментариях, все функции шаблона по умолчанию равны inline.
+4

Что-то для рассмотрения: это звучит как хороший случай преждевременной оптимизации. Из-за этой архитектуры возникают проблемы с производительностью? Если нет, вы должны просто делать то, что имеет смысл семантически. –

+1

Для целых чисел передача по ссылке почти такая же эффективная, как и передача по значению, поэтому константная ссылка хороша для обоих случаев. – dasblinkenlight

+1

«в этом случае, скажем, реализация ingest довольно сложна и не стоит встраивать». В этом случае у вас нет выбора. Поскольку HungryHippo является шаблоном, он должен быть встроен. – cgmb

ответ

8

boost::call_traits заголовок касается именно этой проблемы. Проверьте это here.

В частности, вариант call_traits<T>::param_type включает в себя следующее описание:

Если T небольшой встроенный тип или указатель, то param_type определяется , как T const вместо T const&. Это может улучшить способность компилятора оптимизировать циклы в теле функции, если они соответствуют по переданному параметру, семантика переданного параметра равна , в противном случае без изменений (требуется частичная специализация).

В вашем случае, вы могли бы определить ingest следующим образом:

template <class T> 
class HungryHippo { 
public: 
    void ingest(call_traits<T>::param_type object); 
    // "object" will be passed-by-value for small 
    // built-in types, but passed as a const reference 
    // otherwise 
private: 
    ... 
}; 

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

+2

Как обычно, люди Boost об этом подумали. : D Похоже, что короткий ответ «нет, нет способа уговорить компилятор сделать это за вас», а длинный ответ «хорошо, Boost разработал способ обойти эту проблему в некоторых случаях». Благодаря! – Calvin

0

Хотя трюки, такие как упомянутые выше boost call_traits<T>, делают то, что они утверждают в этом случае, я думаю, вы считаете, что компилятор уже не делая эту оптимизацию в наиболее важных случаях. В конце концов, это тривиально.Если вы принимаете const T& и sizeof(T) <= sizeof(void*), инварианты, налагаемые семантикой ссылок C++, позволяют компилятору просто подставлять значение в тело вашей функции, если это выигрыш. Если это не так, накладные расходы вашего наихудшего случая - это один разворот указателя на аргумент в прологе функции.

+2

Я не верю, что то, что вы говорите о «семантике ссылок C++, позволяет компилятору просто подставить значение по всему телу вашей функции». Рассмотрим http://pastebin.com/xqyrQtxW ... По вашим правилам выход будет равен 10, а по правилам _real_ вывод будет _is_ 5. Вы * не можете * заменить const X и на X. Как я уже сказал в предыдущем комментарии, «const» означает, что вы не можете изменить значение, а не то, что значение не может измениться. – Calvin

+1

@ Калвин Ну ведь Мэтью Холл прав. Функция может принимать X вместо const X &, но в некоторых конкретных условиях - например. когда вы не можете определить адрес X (это rvalue), то вы все равно можете передать его функции, принимающей const X &, и это работает. См. Пример https://ideone.com/6KCeve <- здесь вы не можете взять адрес 20, и вы не можете создать lreference для него, так как это значение rvalue. – DawidPi

+0

Да, есть случаи, когда эта оптимизация легальна. Однако, поскольку это не всегда * законно (см. Мой пример), вы почти никогда не увидите, чтобы компилятор делал это. Чаще всего это происходит, если процедура встроена и ссылка может быть устранена после вставки. – Calvin

Смежные вопросы