const float & x = что-то; // считается вредным? - PullRequest
4 голосов
/ 11 апреля 2009

Был такой код:

// Convenience to make things more legible in the following code
const float & x = some.buried.variable.elsewhere;

// Go on to use x in calculations...

Мне сказали, что "const float &" является "плохим" и должен быть просто обычным или const float.

Я, однако, не мог придумать вескую причину, кроме "Вы не должны печатать" & ".

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

Другими словами, изначально я мог справедливо сказать:

assert(&x == &some.buried.variable.elsewhere)

Тогда как во втором случае я не могу.

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

Может ли кто-нибудь дать мне примеры, где версия "const float &" хуже, чем обычная "float" или "const float"?

Ответы [ 10 ]

5 голосов
/ 11 апреля 2009

Я не могу вспомнить причину, по которой const float & будет лучше, чем const float.

Ссылки имеют смысл, если вы либо беспокоитесь о том, как делаются копии (что не имеет отношения к примитивному типу, например float), либо хотите иметь возможность обновлять значение во всех экземплярах, имеющих общую ссылку (что не имеет значения с const участниками).

Кроме того, ссылки в элементах - огромная боль в шее *, когда дело доходит до инициализации, и поэтому им придется предложить значительное преимущество альтернатив, чтобы быть полезными, и это явно не чехол с const float.


* FQA по ссылкам всегда забавен и заставляет задуматься

4 голосов
/ 11 апреля 2009

Вы должны использовать ссылки только в следующих случаях: (Если я не забыл один)

  1. Указанный тип не маленький и вызывает проблемы с производительностью.
  2. Вы хотите, чтобы ваш локальный псевдоним обновлялся при обновлении ссылочного значения.
    Или: Вы не хотите делать копию.
  3. Требуется возможность обновить другое значение. (Если оно не постоянное)

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

Итак, вы бы хотели:

const float x = some.buried.variable.elsewhere;
2 голосов
/ 11 апреля 2009

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

1 голос
/ 11 апреля 2009

Переписано для наглядности:

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

На большинстве процессоров регистры работают быстро, доступ к памяти - нет. Поэтому мы никогда не хотим получать доступ к памяти, если мы можем избежать этого. Примитивные типы (int, float и т. Д.) Часто входят в регистры. Компилятор полностью контролирует, какие данные находятся в каждом регистре, и может гарантировать, что ничто не будет перезаписывать значение, поэтому переменные примитивных типов могут потенциально оставаться в регистрах в течение длительного времени, пока компилятору не потребуется записывать какие-либо изменения обратно в память.

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

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

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

1 голос
/ 11 апреля 2009

Краткий ответ: этот код правильный, оставьте его таким!

Длинный ответ:

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

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

Но стандарт C ++ гласит, что компилятор не должен освобождать временное возвращаемое значение функции, если на нее "ссылаются на const". Поэтому, если ваша функция возвращает значение с плавающей запятой по значению, вы можете сохранить это значение в ссылке с плавающей запятой, компилятор гарантирует, что фактическое временное значение, возвращаемое функцией, не будет освобождено, пока вы не закончили работу со своей ссылкой.

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

Взгляните на эту статью Херба Саттера для более подробного объяснения.

1 голос
/ 11 апреля 2009

Вы упомянули, что думали, что компилятор может избежать выделения стекового пространства для ссылки. Это может быть верно, если у вас включена оптимизация, но компилятор также может оптимизировать «const float x», сохраняя значение в регистре FP вместо стека. Это то, что GCC делает на x86, например.

1 голос
/ 11 апреля 2009

Вам не нужно сильно беспокоиться об использовании стека, особенно для одного плавающего.

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

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

1 голос
/ 11 апреля 2009

Нет причины, по которой эта ссылка была бы неправильной или плохой. Если вам нужно локальное сокращенное имя («псевдоним»), то ссылки . Если вы скопируете float в новую переменную, вы получите новый объект и, как вы указали, конечно, вы также получите новый адрес: совсем не то, что вы хотели бы. Возьмем для этого ссылку:

float &x = some.buried.variable.elsewhere;

Который будет вести себя как исходная переменная в выражениях.

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

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

float const &x = 3.0; // non-sense. don't use reference here

То же самое с результатом вызова функции, конечно

float const &x = get_some_float(); // non-sense too.
0 голосов
/ 11 апреля 2009

Что следует иметь в виду: ссылка на const не обязательно означает, что то, на что вы ссылаетесь, не изменится в какой-то момент. Это просто означает, что вы не будете изменять ссылочные данные через эту ссылку.

Таким образом, кто-то может теоретически изменить значение some.buried.variable.elsewhere, и тогда чтение x будет отражать это обновленное значение. (При условии соблюдения обычных правил наложения имен и т. Д. И т. Д. По сути, он будет обновлен при следующем прочтении значения x.)

(Не стесняйтесь комментировать, если я что-то не так, здесь, я буду обновлять или удалять ответ.)

0 голосов
/ 11 апреля 2009

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

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

С константой проблем нет.

Можете ли вы объяснить, что вы пытаетесь сделать, или дать больше контекста?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...