Подтвердить что ты не робот

"const T & arg" против "T arg"

Если у вас есть функция, которая принимает, например, int, что является лучшим способом объявления функции и почему она лучше, чем другая?

void myFunction (const int &myArgument);

или

void myFunction (int myArgument);
4b9b3361

Ответ 1

Используйте const T & arg, если sizeof(T)>sizeof(void*) и используйте T arg, если sizeof(T) <= sizeof(void*)

Ответ 2

Они делают разные вещи. const T& делает функцию переменной ссылкой переменной. С другой стороны, T arg вызовет конструктор копирования объекта и передает копию. Если конструктор копирования недоступен (например, private), T arg не будет работать:

class Demo {
    public: Demo() {} 
    private: Demo(const Demo& t) { } 
};

void foo(Demo t) { }

int main() {
    Demo t;
    foo(t); // error: cannot copy `t`.
    return 0;
}

Для небольших значений, таких как примитивные типы (где все вопросы - это содержимое объекта, а не фактическая ссылочная идентификация, скажем, это не дескриптор или что-то еще), обычно T arg. Для больших объектов и объектов, которые вы не можете копировать и/или сохранять ссылочный идентификатор, важно (независимо от размера), рекомендуется передать ссылку.

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

Ответ 3

Взято из Переместить конструкторы. Мне нравятся простые правила

  • Если функция намеревается изменить аргумент как побочный эффект, возьмите его по ссылке/указателю на неконстантный объект. Пример:

    void Transmogrify(Widget& toChange);
    void Increment(int* pToBump);
    
  • Если функция не изменяет свой аргумент и аргумент имеет примитивный тип, возьмите его по значению. Пример:

    double Cube(double value);
    
  • В противном случае

    3,1. Если функция всегда делает копию своего аргумента внутри, возьмите ее по значению.

    3,2. Если функция никогда не делает копию своего аргумента, возьмите ее ссылкой на const.

    3.3. Добавленный мной: Если функция иногда делает копию, то решите, что происходит в кишке: если копия выполняется почти всегда, тогда возьмите по значению. Если копия выполняется половину времени, перейдите по безопасному пути и возьмите ссылку на const.

В вашем случае вы должны взять значение int по значению, потому что вы не намерены изменять аргумент, а аргумент имеет примитивный тип. Я думаю о "примитивном типе" как о неклассическом типе или типе без пользовательского конструктора копирования, а где sizeof(T) - всего лишь пара байтов.

Ответ 4

Существует популярный совет, в котором говорится, что метод передачи ( "по значению" vs "по ссылке const" ) следует выбирать в зависимости от фактического размера типа, который вы собираетесь передать. Даже в этом обсуждении у вас есть ответ, обозначенный как "правильный", который предлагает именно это.

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

Решения, основанные на фактических размерах объектов, зависящих от реализации, должны быть предоставлены компилятору как можно чаще. Пытаясь "адаптировать" ваш код к этим размерам, жестко кодируя метод прохождения, это совершенно контрпродуктивная трата усилий в 99 случаях из 100. (Да, это правда, что в случае языка С++ компилятор не имеют достаточно свободы для использования этих методов взаимозаменяемо - на самом деле они не являются взаимозаменяемыми в С++ в общем случае. Хотя, при необходимости, правильный метаопрограммирование с использованием макросов [semi-] автоматически может быть реализовано посредством метапрограммирования шаблонов, но это другая история).

Более значимый критерий выбора метода передачи при написании кода "вручную" может звучать следующим образом:

  • Предпочитают передавать "по значению" , когда вы передаете атомный, унитарный, неделимый объект, такой как одно неагрегатное значение любого типа - число, указатель, итератор. Обратите внимание, что, например, итераторы являются унитарными значениями на логическом уровне. Поэтому, предпочитайте передавать итераторы по значению, независимо от того, превышает ли их фактический размер sizeof (void *). (Реализация STL делает именно это, BTW).

  • Предпочитают передавать "по ссылке const", когда вы передаете агрегированное, составное значение любого типа. то есть значение, которое на логическом уровне явно выражено "составной" природой, даже если его размер не превышает sizeof (void *).

Разделение между ними не всегда ясно, но, как всегда, со всеми такими рекомендациями. Более того, разделение на "атомарные" и "сложные" объекты может зависеть от специфики вашего дизайна, поэтому решение может фактически отличаться от одного проекта к другому.

Обратите внимание, что это правило может принимать решения, отличные от решений якобы "правильного" метода на основе размера, упомянутого в этом обсуждении.

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

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


В С++ 11 введение семантики перемещения в язык произвело заметный сдвиг в относительных приоритетах различных методов передачи параметров. При определенных обстоятельствах вполне возможно передать даже сложные объекты по значению

Должны ли все/большинство функций setter в С++ 11 записываться как шаблоны функций, принимающие универсальные ссылки?

Ответ 5

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

Изменить: (в основном в ответ на комментарии Джеффа Харди): Это правда, что передача по ссылке const является, вероятно, "самой безопасной" альтернативой при наибольшем числе обстоятельств, но это не значит, что всегда лучше всего делать, Но, чтобы понять, что здесь обсуждается, вам действительно нужно внимательно прочитать всю статью Дэйва, поскольку она довольно техническая, а аргументация ее выводов не всегда интуитивно очевидна (и вам нужно понять, что нужно делать разумные выборы).

Ответ 6

Обычно для встроенных типов вы можете просто пройти по значению. Они небольшие типы.

Для пользовательских типов (или шаблонов, когда вы не собираетесь передавать) предпочитаете const &. Размер ссылки, вероятно, меньше размера этого типа. И это не приведет к дополнительной копии (без вызова конструктора копирования).

Ответ 7

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

Ответ 8

Ссылка на const T не стоит усилий при наборе текста в случае таких скалярных типов, как int, double и т.д. Правило большого пальца состоит в том, что типы классов должны приниматься посредством ref-to-const. Но для итераторов (которые могут быть типами классов) мы часто делаем исключение.

В общем коде вы должны, вероятно, написать "T const &" большую часть времени, чтобы быть в безопасности. Кроме того, повысить характеристики вызова, вы можете использовать для выбора наиболее перспективного типа передачи параметров. Насколько мне известно, он в основном использует ref-to-const для типов классов и pass-by-value для скалярных типов.

Но есть также ситуации, когда вы можете принять параметры по значению, независимо от того, насколько дорого может быть создание копии. См. Статью Дейва "Хотите скорость? Используйте пропуск по значению!" .

Ответ 9

Для простых типов, таких как int, double и char *, имеет смысл передать его по значению. Для более сложных типов я использую const T & если нет конкретной причины не делать этого.

Стоимость прохождения параметра 4 - 8 байтов будет настолько низкой, насколько вы можете получить. Вы ничего не покупаете, передавая ссылку. Для более крупных типов передача их по значению может быть дорогостоящей.

Ответ 10

Это не будет иметь никакого значения для int, поскольку, когда вы используете ссылку, адрес памяти еще должен быть передан, а адрес памяти (void *) обычно имеет размер целого числа.

Для типов, содержащих много данных, он становится намного более эффективным, поскольку он позволяет избежать огромных накладных расходов от копирования данных.

Ответ 11

Ну, разница между этими двумя значениями не очень важна для ints.

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

В обоих случаях вы видите это в вызывающем

myFunct(item);

Для вызывающего, элемент не будет изменен myFunct, но пропуск по ссылке не будет нести расходы на создание копии.

Существует очень хороший ответ на аналогичный вопрос в Pass by Reference/Value в С++

Ответ 12

Разница между ними заключается в том, что вы передаете int (который копируется), а один использует существующий int. Поскольку это a const ссылка, она не изменяется, поэтому она работает практически одинаково. Большая разница здесь в том, что функция может изменять значение int локально, но не ссылку const. (Я полагаю, что идиот мог бы сделать то же самое с const_cast<>, или, по крайней мере, попытаться.) Для более крупных объектов я могу думать о двух отличиях.

Во-первых, некоторые объекты просто не могут быть скопированы, auto_ptr<> и объекты, содержащие их, являются очевидным примером.

Во-вторых, для больших и сложных объектов быстрее передать ссылку const, чем копировать. Это обычно не очень важно, но передача объектов с помощью ссылки const является полезной привычкой.

Ответ 13

Все работает отлично. Не теряйте время, беспокоясь об этом.

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

Ответ 14

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

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

Мораль: напишите свои конструкторы копирования.