Должны ли небольшие простые структуры передаваться по константной ссылке?

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

void foo(std::string str);//bad
void foo(const std::string &str);//good

Но сегодня я подумал, что, возможно, на самом деле некоторые простые пользовательские типы лучше передавать по значению, например:

class Vector2
{
public:
    float x, y;
    ...constructors, operators overloads, utility methods, etc...
};

void foo(Vector2 pos);
void foo(const Vector2 &pos);//is this really better than by value?
void foo(float x, float y);//after all isn't by value effectively doing this?

Я думаю, что передача Vector2 по ссылке на самом деле дороже, чем передача по значению, поскольку компилятор теперь использует указатель и разыменовывает для доступа к версии const Vector2 &pos?

Так ли это? Простые объекты лучше всего передавать по значению? Где должна быть проведена линия?


person Fire Lancer    schedule 22.07.2010    source источник
comment
Обратите внимание, что если ваш тип класса имеет объявленные пользователем конструкторы, это не POD.   -  person James McNellis    schedule 23.07.2010
comment
Общее эмпирическое правило: передавать по константной ссылке, если только sizeof(T) <= sizeof(void*) или T не являются примитивными.   -  person GManNickG    schedule 23.07.2010
comment
@GMan, ты меня опередил (и +1). Хотя я бы обобщил, что он, вероятно, меньше или равен размеру слова процессора. И нужно проверить характеристики процессора, чтобы узнать оптимальный размер. Если я не ошибаюсь, компиляторы потенциально могут оптимизировать константную ссылку для передачи по значению в некоторых случаях, независимо от размера. Я полагаю, что это особенно верно в C++0x, учитывая новую семантику перемещения.   -  person Nathan Ernst    schedule 23.07.2010
comment
@James Я предполагаю, что текущий стандарт C++03 не соответствует, хотя, судя по всему, C++0x будет считать это POD.   -  person Fire Lancer    schedule 23.07.2010
comment
и я думаю, что в очень редких случаях ваш объект будет меньше, чем размер слова процессора и / или размер (void *) ... @Fire, вам действительно нужно нарисовать очень тонкую линию ;-)   -  person KedarX    schedule 23.07.2010


Ответы (6)


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

person Peter G.    schedule 22.07.2010

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

В мире C# вы принимаете это решение, выбирая, сделать ли что-то классом или структурой. Классы используют семантику ссылок, а структуры используют семантику значений. Все, что я когда-либо читал, говорит о том, что вы должны делать классом все, если только оно не очень маленькое, то есть порядка 16 байт. Если вы ищете отсечку для передачи по значению, а не по константной ссылке в C++, 16 байтов кажутся разумным порогом.

person Matt Davis    schedule 22.07.2010
comment
Типы POD не имеют нетривиальных конструкторов копирования (или даже нетривиальных конструкторов или деструкторов по умолчанию). - person Ben Voigt; 23.07.2010
comment
ОП не сказал явно тип POD. Он сказал простой определяемый пользователем тип, а затем привел пример, показывающий, что у него есть конструктор. - person Matt Davis; 23.07.2010

Просто из соображений политики я передаю любой объект, который не является базовым типом данных C, по ссылке const. Так гораздо легче запомнить.

person T.E.D.    schedule 22.07.2010

Более старый, но ясный анализ тонкостей передачи параметров можно найти по адресу http://www.ddj.com/184403855. Конечно, c++0x устраняет многие такие проблемы с семантикой перемещения, но статья дает много обоснований того, почему семантика перемещения желательна.

person rlduffy    schedule 23.07.2010

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

person supercat    schedule 23.07.2010

Я думаю, что передача Vector2 по ссылке на самом деле дороже, чем передача по значению, поскольку компилятор теперь использует указатель и разыменовывает для доступа к версии const Vector2 &pos.

Было бы верно, если бы вы передали объект, где size_of(object) < size_of(pointer_to_object). Например, char const& может быть дороже, чем char.

person a1ex07    schedule 22.07.2010
comment
Это также может быть правдой, если повторное разыменование приведет к большему количеству накладных расходов, чем можно сэкономить, не передавая столько данных. Или если ссылка препятствует оптимизации из-за проблем с псевдонимами. Или если ссылка вредит вашему населенному пункту. - person Dennis Zickefoose; 23.07.2010