boost :: graph function с реберным итератором, переданным по значению и графу, переданным по ссылке [duplicate]

В веб-приложении каждая задача выполняется в виде запроса и ответа.

Программирование на стороне клиента - это HTML-код с Java-скриптом и его фреймворками, библиотеки выполняются в Internet Explorer, Mozilla, Chrome-браузерах. В сценарии Java-сценария серверные сервлеты программирования выполняются в Tomcat, web-логике, j боссе, WebSphere severs

187
задан Kevin 6 November 2008 в 22:47
поделиться

10 ответов

Обычно рекомендуется использовать наилучшую практику1 для использования pass by const ref для всех типов , за исключением встроенных типов (char, int, double и т. д.), для итераторы и объекты функций (lambdas, классы, полученные из std::*_function).

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

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

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

. В этих ситуациях мы имеем следующие (упрощенные) off:

  1. Мы можем передать объект по ссылке, затем скопировать внутренне.
  2. Мы можем передать объект по значению.

«Pass by value» по-прежнему вызывает копирование объекта, если объект не является rvalue. В случае rvalue объект может быть перемещен вместо этого, так что второй случай внезапно перестает «копировать, а затем перемещать», а «перемещать», затем (потенциально) перемещаться снова ».

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


Историческое примечание:

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

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

Но в целом компилятор не может это определить, и появление семантики перемещения в C ++ делает эту оптимизацию менее актуальной.


1 Например в Scott Meyers, Эффективный C ++ .

2 Это особенно часто относится к конструкторам объектов, которые могут принимать аргументы и хранить их внутри себя, чтобы быть частью состояния построенного объекта.

172
ответ дан Community 26 August 2018 в 07:18
поделиться
  • 1
    hmmm ... Я не уверен, что стоит пройти ref. дважды s – sergtk 6 November 2008 в 23:11
  • 2
    Как обычно, поддержка помогает здесь. boost.org/doc/libs/1_37_0/libs/utility/call_traits.htm имеет материал шаблона, который автоматически определяет, когда тип является встроенным (полезно для шаблонов, где вы иногда не можете легко это знать ). – CesarB 7 November 2008 в 01:02
  • 3
    Этот ответ пропустит важный момент. Чтобы избежать нарезки, вы должны пройти по ссылке (const или иначе). См. stackoverflow.com/questions/274626/… – ChrisN 15 November 2008 в 19:08
  • 4
    @ Крис: правильно. Я оставил всю часть полиморфизма, потому что это совершенно другая семантика. Я считаю, что OP (семантически) означало передачу аргумента «по значению». Когда требуется другая семантика, вопрос даже не представляет себя. – Konrad Rudolph 15 November 2008 в 19:43

Изменить: Новая статья Дейва Абрахама на cpp-next:

Хотите скорость? Передача по значению.


Передавать по значению для структур, где копирование дешево, имеет дополнительное преимущество, заключающееся в том, что компилятор может предположить, что объекты не являются псевдонимами ( не те же объекты). Используя pass-by-reference, компилятор не может всегда это предполагать. Простой пример:

foo * f;

void bar(foo g) {
    g.i = 10;
    f->i = 2;
    g.i += 5;
}

компилятор может оптимизировать его в

g.i = 15;
f->i = 2;

, так как он знает, что f и g не имеют одного и того же местоположения. если g была ссылкой (foo & amp;), компилятор не мог этого предположить. так как gi может быть затем сглажено f-> i и должно иметь значение 7. поэтому компилятор должен будет повторно извлечь новое значение gi из памяти.

Для более пратических правил здесь является хорошим набором правил, найденным в статье Move Constructors (рекомендуемое чтение).

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

«Примитив» означает, что в основном небольшие типы данных, длина которых несколько байтов и не являются полиморфными (итераторы, функциональные объекты и т. д.) или дорогими для копирования. В этой статье есть еще одно правило. Идея состоит в том, что иногда хочется сделать копию (в случае, если аргумент не может быть изменен), а иногда и не нужен (в случае, если кто-то хочет использовать сам аргумент в функции, если аргумент был временным в любом случае , например). В документе подробно объясняется, как это можно сделать. В C ++ 1x этот метод может быть использован изначально с поддержкой языка. До тех пор я бы пошел с вышеуказанными правилами.

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

my::string uppercase(my::string s) { /* change s and return it */ }

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

bool all_uppercase(my::string const& s) { 
    /* check to see whether any character is uppercase */
}

Однако, если вы предназначаете параметр для записи чего-либо в аргумент, то передайте его по неконстантной ссылке

bool try_parse(T text, my::string &out) {
    /* try to parse, write result into out */
}
89
ответ дан ahcox 26 August 2018 в 07:18
поделиться
  • 1
    я нашел ваши правила хорошими, но я не уверен в первой части, где вы говорите о том, чтобы не передавать ее как ref, ускорит ее. да, конечно, но не передавая что-то в качестве ссылки, просто для оптимизма не имеет смысла вообще. если вы хотите изменить объект стека, который вы проходите, сделайте это по ссылке. если вы не передадите его по значению. если вы не хотите изменить его, передайте его как const-ref. оптимизация, которая поставляется с передачей по значению, не должна иметь значения, поскольку вы получаете другие вещи при передаче как ref. я не понимаю, нужна ли «скорость»? Если вы собираетесь выполнять эти операции, вы все равно будете передавать по значению. – chikuba 2 May 2012 в 09:14
  • 2
    Johannes: I любил эту статью, когда я ее читал, но я был разочарован, когда попробовал. Этот код не удался как для GCC, так и для MSVC. Я что-то пропустил, или это не работает на практике? – Mehrdad 14 August 2012 в 22:50
  • 3
    Я не думаю, что согласен, что если вы все равно хотите сделать копию, вы передадите ее по значению (вместо const ref), а затем переместите его. Посмотрите на это таким образом, что более эффективно, копия и ход (у вас даже может быть 2 копии, если вы передадите их вперед) или просто копию? Да, есть какие-то особые случаи для обеих сторон, но если ваши данные не могут быть перемещены в любом случае (например, POD с целыми числами), нет необходимости в дополнительных копиях. – Ion Todirel 14 October 2012 в 07:29
  • 4
    Мехрдад, не уверен, чего вы ожидали, но код работает так, как ожидалось – Ion Todirel 14 October 2012 в 07:35
  • 5
    Я бы подумал о необходимости копирования только для того, чтобы убедить компилятор, что типы не перекрывают недостаток в языке. Я предпочитаю использовать GCC __restrict__ (который также может работать над ссылками), чем избыточные копии. Слишком плохой стандарт C ++ не использовал ключевое слово C99 restrict. – Ruslan 9 July 2017 в 07:31

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

пример void amount(int account , int deposit , int total )

входной параметр: учетная запись, параметр выходного выхода: общий

вход и выход - другой вызов использования vaule

  1. void amount(int total , int deposit )

общий суммарный вывод депозита

-5
ответ дан Alex 26 August 2018 в 07:18
поделиться

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

5
ответ дан GeekyMonkey 26 August 2018 в 07:18
поделиться
  • 1
    Я не уверен, почему это было отклонено? Это имеет смысл для меня. Если вам понадобится значение, хранящееся в данный момент, перейдите по значению. Если нет, передайте ссылку. – Totty 6 November 2008 в 22:46
  • 2
    Он полностью зависит от типа. Выполнение типа POD (простые старые данные) по ссылке может фактически снизить производительность, вызвав больше доступа к памяти. – Torlack 6 November 2008 в 22:49
  • 3
    Очевидно, что передача int по ссылке ничего не спасает! Я думаю, что вопрос подразумевает вещи, которые больше, чем указатель. – GeekyMonkey 6 November 2008 в 22:55
  • 4
    Это не так очевидно, я видел много кода у людей, которые по-настоящему не понимают, как компьютеры работают с простыми вещами со стороны const ref, потому что им сказали, что это лучшее, что нужно делать. – Torlack 6 November 2008 в 22:57

Пропустить по значению для малых типов.

Пропустить ссылки const для больших типов (определение большого может варьироваться между машинами). НО, в C ++ 11, переходите по значению, если вы собираетесь потребляйте данные, так как вы можете использовать семантику перемещения. Например:

class Person {
 public:
  Person(std::string name) : name_(std::move(name)) {}
 private:
  std::string name_;
};

Теперь вызывающий код будет делать:

Person p(std::string("Albert"));

И только один объект будет создан и перемещен непосредственно в член name_ в классе Person , Если вы перейдете по ссылке const, необходимо будет скопировать его в name_.

1
ответ дан Germán Diago 26 August 2018 в 07:18
поделиться

Зависит от типа. Вы добавляете небольшие накладные расходы, чтобы сделать ссылку и разыменовать. Для типов с размером, равным или меньшим, чем указатели, использующие копию ctor по умолчанию, вероятно, быстрее перейти по значению.

12
ответ дан Lou Franco 26 August 2018 в 07:18
поделиться
  • 1
    Для не-родных типов вы можете (в зависимости от того, насколько хорошо оптимизирует код компилятора) получить увеличение производительности, используя ссылки на const вместо ссылок. – OJ. 6 November 2008 в 23:06

Это то, что я обычно работаю при разработке интерфейса функции без шаблона:

  1. Пропустить по значению, если функция не хочет изменять параметр, а значение дешево для copy (int, double, float, char, bool и т. д.) Обратите внимание, что std :: string, std :: vector и остальные контейнеры в стандартной библиотеке НЕ)
  2. Pass by const, если значение дорогое копировать, и функция не хочет изменять указанное значение, а NULL - это значение, которое обрабатывает функция.
  3. Пропускать указатель не const, если значение является дорогостоящим копировать, а функция хочет изменить указанное значение, а NULL - это значение, которое обрабатывает функция.
  4. Передавать по ссылке const, когда значение дорого копировать, и функция не хочет изменять указанное значение to и NULL не будут действительным значением, если вместо этого использовался указатель.
  5. Пропускайте по неконстантной ссылке, когда значение дорого копируется, и функция хочет изменить значение и NULL не будут действительным значением, если вместо этого использовался указатель.
4
ответ дан Martin 26 August 2018 в 07:18
поделиться

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

1
ответ дан Peter 26 August 2018 в 07:18
поделиться
  • 1
    Я понимаю, что std::vector фактически выделяет свои элементы в куче, и сам векторный объект никогда не растет. Подожди. Если операция приводит к созданию копии вектора, тем не менее, он фактически переместит и дублирует все элементы. Это было бы плохо. – Steven Lu 26 January 2012 в 00:55
  • 2
    Да, это то, о чем я думал. sizeof(std::vector<int>) является постоянным, но передача его значением по-прежнему будет копировать содержимое в отсутствие умения компилятора. – Peter 31 January 2012 в 11:56

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

4
ответ дан sergtk 26 August 2018 в 07:18
поделиться
  • 1
    Если вам нужно изменить копию параметра вызываемого, вы можете сделать копию в вызываемом коде, а не передавать по значению. ИМО, вы, как правило, не должны выбирать API на основе такой детали реализации: источник вызывающего кода одинаковый в любом случае, но его объектный код не является. – Steve Jessop 7 November 2008 в 01:17
  • 2
    Если вы пройдете по значению, то будет создана копия. И ИМО не имеет значения, каким образом вы создаете копию: через аргумент, передаваемый по значению или локально - это то, что касается C ++. Но с дизайнерской точки зрения я согласен с вами. Но я описываю возможности C ++ только здесь и не трогаю дизайн. – sergtk 7 November 2008 в 21:17

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

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

Если вы занимаетесь программированием шаблонов, вы обычно вынуждены всегда передавать const ref, поскольку вы не знаете типов Передача штрафов за передачу чего-то плохого по значению намного хуже, чем штрафы за прохождение встроенного типа по const ref.

8
ответ дан Torlack 26 August 2018 в 07:18
поделиться
  • 1
    Примечание по терминологии: структура, содержащая миллион ints, все еще является типом POD. Возможно, вы имеете в виду «для встроенных типов лучше всего передавать по значению». – Steve Jessop 7 November 2008 в 01:18
  • 2
    Терминология исправлена. – Torlack 8 November 2008 в 18:36
  • 3
    Отличное объяснение механизма за сценой! +1 – kizzx2 16 July 2010 в 10:51
Другие вопросы по тегам:

Похожие вопросы: