Constexpr глобальные константы в файле заголовка и odr

К сожалению, я несколько смущен насчет constexpr, глобальных констант, объявленных в файлах заголовков, и odr.

Короче: можем ли мы заключить здесь

https://isocpp.org/files/papers/n4147.pdf

что

constexpr MyClass const MyClassObj () { return MyClass {}; }
constexpr char const * Hello () { return "Hello"; }

предпочтительнее

constexpr MyClass const kMyClassObj = MyClass {};
constexpr char const * kHello = "Hello";

для определения глобальных переменных в файле заголовка если я хочу "просто использовать" те объявленные глобально объявленные объекты и не хочу думать о том, как я их использую?

Ответы

Ответ 1

Примечание. Начиная с С++ 17 вы можете объявить переменные как встроенные.


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

Основная проблема заключается в том, что переменные const в области пространства имен, такие как ваши (обычно), имеют внутреннюю привязку ([basic.link]/(3.2)). Это означает, что каждая единица перевода, компилирующая соответствующий заголовок, будет наблюдать другой объект (т.е. Символ).

Теперь представьте, что у нас есть шаблон или встроенная функция в заголовке с использованием этих объектов. ODR очень точно описывает этот сценарий - [basic.def.odr]/6:

введите описание изображения здесь

", инициализированное постоянным выражением", безусловно, встречается, так как мы говорим constexpr. Таким образом, "объект имеет то же значение во всех определениях D", если вы не обезьяны.

"объект не используется odr", вероятно, является единственным сомнительным условием. В принципе, это требует, чтобы вы не требовали существования среды исполнения в качестве символа, что, в свою очередь, подразумевает, что

  • Вы не привязываете его к ссылке (= > вы ее не пересылаете!)

  • Вы не выполняете (не прямо или неявно) свой адрес.

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

Точнее, odr-use определяется [basic.def.odr]/3:

Переменная x, имя которой отображается как потенциально оцениваемое выражение ex, является odr-используемым ex, если не применяется преобразование lvalue-to-rvalue (4.1) в x дает константное выражение (5.20), которое не вызывает никаких нетривиальных функций и, если x является объектом, ex является элементом множества потенциальные результаты выражения e, где либо lvalue-to-rvalue conversion (4.1) применяется к e, либо e является выражением отбрасываемого значения (пункт 5).

Применение l-t-r к любой переменной constexpr будет вести себя так, как требуется первой части. Вторая часть требует, чтобы переменная использовалась как значение, а не фактический объект; то есть он в конечном итоге либо отбрасывается, либо оценивается напрямую, что дает вышеописанные эмпирические правила.

Если вы избегаете использования переменной odr внутри встроенных функций, шаблонов и т.п., вы в порядке. Но если вы используете возвращаемое значение соответствующей функции constexpr, вам не придется беспокоиться, так как prvalues ​​уже ведут себя как значения/литералы (а не объекты), а функции constexpr являются встроенными и определенно не будут нарушать ODR (если вы не используете там переменные constexpr!).