Const-корректность для нетривиальных переменных

(отчасти вдохновленный этим ответом, хотя и не связанным)

Мне всегда говорили (и я говорил), что сохранение const-правильности даже для недолговечных переменных является ценным и хорошей практикой, например:

const std::string a = "Hello world";

Вместо

std::string a = "Hello world";

Этот:

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

Хотя с тех пор, как в современном C++ появилось исключение копирования, в стандарте появилось несколько предложений., которые позволяют компилятору вызывать конструктор перемещения вместо конструктора копирования:

В следующих контекстах инициализации копирования вместо операции копирования может использоваться операция перемещения:

(3.1) Если выражение в операторе return ([stmt.return]) является (возможно, заключенным в скобки) id-выражением, которое называет объект с длительностью автоматического хранения, объявленной в теле или объявлении параметра- предложение самой внутренней объемлющей функции или лямбда-выражения, или

(3.2) если операндом throw-expression ([expr.throw]) является имя энергонезависимого автоматического объекта (кроме функции или параметра оператора catch), область действия которого не выходит за пределы конец самой внутренней охватывающей try-block (если она есть),

Означает ли это, что на самом деле может быть потеря производительности при использовании объектов const с конструктором копирования/перемещения, отличным от используемого по умолчанию, вместо повышения производительности при столкновении с ситуациями, к которым может применяться такое исключение?


person Hatted Rooster    schedule 12.03.2019    source источник
comment
@Acorn Когда оптимизаторы еще не были такими зрелыми, я определенно видел, что компиляторы генерируют более оптимизированные сборки, чем без const. (Я думаю, что это было до C ++ 11 раз). В настоящее время это может быть не так, поскольку оптимизаторы так хорошо выясняют, изменены ли данные вообще, так что, возможно, это уже не так, и это просто древняя реликвия. . (например, использование inline для повышения производительности).   -  person Hatted Rooster    schedule 12.03.2019
comment
Не уверен, что вы имеете в виду, и, конечно, я не могу это доказать, но это звучит как ошибка codegen (т.е. оптимизация const, когда они не должны были), нестандартная реализация (та же самая) или пропущенная оптимизация ( т. е. не оптимизировать оба случая одинаково).   -  person Acorn    schedule 12.03.2019
comment
@Acorn Если вы берете адрес неконстантного объекта и передаете неконстантный указатель функции, определенной в другой единице перевода, оптимизатор должен предположить, что объект мог быть изменен, и доступ к объекту позже должен осуществляться через память, Правильно? Если объект является константным, то передача неконстантного указателя на него в другую TU не мешает оптимизатору кэшировать значение переменной в регистре, поскольку оно не может измениться. Мои рассуждения неверны?   -  person eerorika    schedule 12.03.2019
comment
@Acorn Вот разница в коде: godbolt.org/z/xMuMVB   -  person Max Langhof    schedule 12.03.2019
comment
Между прочим, есть надуманный случай, когда const действительно может улучшить производительность: extern const достаточно простой объект (например, int) с инициализатором, потому что даже без внутренней компоновки компилятор может использовать тот факт, что стандарт говорит, что значение не разрешено сдача.   -  person Acorn    schedule 12.03.2019
comment
@MaxLanghof Да, это гораздо лучший пример по той же причине, что и мой предыдущий комментарий. Что ж, тогда я в любом случае отзываю :) Не уверен, должен ли я оставить комментарий, так как я больше не могу его редактировать. Тем не менее, обратите внимание, что эти типы переменных ведут себя как переменные constexpr, а не только const, поэтому можно сказать, что единственное преимущество const заключается в тех случаях, когда вместо них может применяться (и должно быть) constexpr.   -  person Acorn    schedule 12.03.2019
comment
@Acorn Я думаю, что в основном это сводится к тому, что константные ссылки / указатели в аргументах функции не имеют значения, поскольку компилятор не может быть уверен в их правдивости с любой стороны, но он может быть уверенным, что объявлены const переменные в данной области не может измениться.   -  person Max Langhof    schedule 12.03.2019
comment
@eeorika Не уверен, что ты имеешь в виду. Если объект const, он может иметь, например, mutable членов. Кроме того, следите за неоригинальными const объектами, потому что они также не дают никаких гарантий. Даже если в нем нет mutable членов и изначально const, выполнение быстрого теста выглядит так, будто оптимизаторы не предполагают, что оно не изменилось. Даже если бы они это сделали, в целом было бы плохой идеей полагаться на const для производительности. Предположим, что вы не получаете никаких преимуществ, и если вы знаете, что объект не меняется, спроектируйте код, чтобы использовать это другим способом.   -  person Acorn    schedule 12.03.2019
comment
@Acorn не должно это позволять (по крайней мере теоретически) оптимизировать test2() до foo(bar()); return true; ? Я могу понять, если у них нет проходов оптимизатора для этого (потому что это обычно бесполезно), но test2() никогда не сможет вернуть false, не так ли? Позвольте мне задать это как вопрос на самом деле ...   -  person Max Langhof    schedule 12.03.2019
comment
@Acorn it may still have mutable members Оптимизацию, которую я предлагаю, нельзя использовать для изменяемых элементов. Но не все члены изменяемы. На самом деле изменяемые члены почти никогда не используются. Конечно, если все ваши члены изменяемы, то я согласен, что использование константных экземпляров этого класса действительно было бы бессмысленным. doing a quick test looks like optimizers do not assume it hasn't changed Пример Макса демонстрирует, как именно это делает оптимизатор (вместо неконстантного указателя используется константная ссылка; это различие не имеет значения).   -  person eerorika    schedule 12.03.2019
comment
@MaxLanghof Да, насколько я понимаю, так и должно быть. Однако похоже, что компиляторы останавливаются, если они не могут использовать постоянное выражение, поэтому эти оптимизации работают только в том случае, если вы могли бы написать constexpr для начала, что в этом случае вы не можете из-за bar().   -  person Acorn    schedule 12.03.2019
comment
@Acorn Ну, по крайней мере, в в этом случае они могут сделать это без constexpr...   -  person Max Langhof    schedule 12.03.2019
comment
@eerorika Я думаю, ты путаешь то, что я сказал. Если объект const, который вы передаете, имеет один член mutable или член с mutable членами, то следует предположить, что они могли измениться, а это, в свою очередь, может означать, что другие методы ведут себя иначе. Что касается примера Макса, то здесь все по-другому: его случай был тривиальным, когда переменные фактически обрабатывались как constexpr. Если вы попытаетесь сделать то же самое с очень простым объектом типа класса, то оптимизации тоже не произойдет (я могу только заставить clang сделать это, если это POD и я обращаюсь к переменной напрямую, без геттера).   -  person Acorn    schedule 12.03.2019
comment
@MaxLanghof Но здесь вы никуда не передаете адрес, поэтому доказать, что никто его не изменяет, тривиально; это другая оптимизация, и это произойдет даже без const.   -  person Acorn    schedule 12.03.2019
comment
@Acorn Правда, я думаю, анализ побега действительно напуган ... godbolt.org/z/Lgvo8O. Задал новый вопрос здесь: stackoverflow .com/questions/55128512/   -  person Max Langhof    schedule 12.03.2019


Ответы (1)


Означает ли это, что на самом деле может быть потеря производительности при использовании объектов const с конструктором копирования/перемещения, отличным от используемого по умолчанию?

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

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

person eerorika    schedule 12.03.2019