переменная constexpr в области именного пространства без явного встроенного определения и с - PullRequest
1 голос
/ 23 сентября 2019

Даже после прочтения этого вопроса о неявных переменных области действия встроенного пространства имен , определенных в заголовках, я немного параноидален из-за того, что явные переменные области действия встроенного пространства имен в порядке, поскольку нарушения AFAIK в отношении ODR являются UB, а недиагноз не требуется.Верно ли мое понимание того, что явно встроенные указанные constexprconst энергонезависимые) определенные переменные в области именного пространства являются встроенными переменными , и поэтому их использование ODR в порядке при использовании в различных единицах перевода?Даже cppreference.com противоречит самому себе, когда иногда говорят, что встроенные переменные должны быть внешними для исключения использования ODR, в то время как на другой странице в целом допустимы только встроенные переменные внутренней связи, а внешние - только с дополнительными требованиями.

В принципе, правильны ли эти предположения?:

/*! @file some_header.hpp */
#ifndef HEADER_GUARD
#define HEADER_GUARD

constexpr int global_non_expl_inline = 42; //UB
static constexpr int global_non_expl_inline_static = 42; //UB
inline int global_expl_inline = 42; //ok
inline int static global_expl_inline_explicit_static = 42; //? external linkage by default but static explicit but still ok?
inline int extern global_expl_inline_explicit_extern = 42; //UB

namespace foo {
constexpr int global_non_expl_inline = 42; //UB
static constexpr int global_non_expl_inline_static = 42; //UB
inline int global_expl_inline = 42; //ok
inline int static global_expl_inline_explicit_static = 42; //? external linkage by default but static explicit but still ok?
inline int extern global_expl_inline_explicit_extern = 42; //UB
}

namespace {
inline int extern global_expl_inline_explicit_extern_but_unnamed_ns = 42; //ok
}

struct bar{
    static int const in_class_static = 42;//ok
    static int in_class_but_out_of_source_def;
};

int bar::in_class_but_out_of_source_def = 42;//UB

#endif

Ответы [ 2 ]

1 голос
/ 24 сентября 2019

(Поскольку вопрос помечен C ++ 17, я буду использовать черновой вариант стандарта N4659 в качестве справочного материала, что позволяет избежать сложностей, связанных с модулями.)

Во-первых, помнитечто имена в разных единицах перевода относятся к одному и тому же объекту, только если они имеют внешнюю связь ( [basic.link] / 9 ).Имена с внутренней связью всегда относятся к разным сущностям в разных единицах перевода, даже если их определения выглядят одинаково.

Поэтому мы можем разделить эти определения на три группы:

  1. внутренняя связь
    • может появляться в нескольких единицах перевода (не UB);будут определять разные переменные в разных TU
  2. внешняя связь со встроенным спецификатором
    • может появляться в нескольких единицах перевода (не UB);будет определять одну и ту же переменную во всех TU
  3. внешняя связь без встроенного спецификатора
    • не может появляться в нескольких единицах перевода (UB: нарушение ODR)

(Определения в первой группе могут быть проблематичными, если переменная используется в odr в другом определении с внешней связью, что может привести к нарушению [basic.def.odr] / (6.2) .)

Следующие определения относятся к первой группе (внутренняя связь):

  • оба из global_non_expl_inline (Это не встроенная переменная энергонезависимой константной квалификациитипа и не имеет предыдущего объявления. Таким образом, оно соответствует [basic.link] / (3.2) )
  • обоим из global_non_expl_inline_static ( [basic.link] / (3.1) )
  • оба global_expl_inline_explicit_static ( [basic.link] / (3.1) )
  • global_expl_inline_explicit_extern_but_unnamed_ns ( [basic.link] / (4.1) )

Следующие определения относятся ко второй группе (внешняя связь со встроенным спецификатором):

  • оба из global_expl_inline
  • обаglobal_expl_inline_explicit_extern

Следующее определение относится к третьей группе (внешняя связь без встроенного спецификатора):

  • bar::in_class_but_out_of_source_def

(bar::in_class_static не определено, оно может появляться в нескольких TU, но не может использоваться odr без определения.)

1 голос
/ 23 сентября 2019

Ну ... Так как вам действительно удалось запутаться в своем вопросе, я подумал, что займусь этим подробнее.Во-первых, мы должны классифицировать соответствующие свойства переменной: время жизни, видимость, связь. На них влияют ключевые слова: static, inline, constexpr, const, extern, которые вы используете в своем вопросе..

В области имен в определении переменной:
- static: указывает внутреннюю связь
- inline: допускает несколько идентичных определений одной и той же переменной в разных единицах перевода и гарантирует, что они будутссылаются на один и тот же объект (например, имеют одинаковые адреса)
- constexpr: подразумевает const - const: по умолчанию используется внешняя связь
- extern: указывает внешнюю связь

Таким образом,
- global_non_expl_inline: по умолчанию используется внешняя связь.Нет проблем, если только другой модуль перевода не определит другую такую ​​переменную с внешней связью.
- global_non_expl_inline_static: внутренняя связь.Хорошо, если вы нигде не определяете другие такие переменные.
- global_expl_inline: Внешняя связь и inline.Нет проблем, если только другой модуль перевода не объявит другую такую ​​переменную без inline.
- global_expl_inline_explicit_static: Хорошо, переменная static inline имеет смысл, если вы не хотите, чтобы она была доступна во время ссылки, но хотитеодна и та же переменная во всех ваших единицах перевода - например, полезна для всех видов констант.
- global_expl_inline_explicit_extern: внешняя связь и inline.Нет проблем, если только другой модуль перевода не объявит другую такую ​​переменную без inline.
- global_expl_inline_explicit_extern_but_unnamed_ns: внутренняя связь в соответствии с cppreference .

На уровне класса:
- in_class_static: внешняя связь.Хорошо, согласно cppreference , но требует объявления в области имен пространства, если оно используется odr.
- in_class_but_out_of_source_def: внешняя связь.Также хорошо.Это на самом деле стандартный способ.

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

Что касается вашего комментария по этому вопросу: я не могу воспроизвести проблему, а и другие люди тоже не могутв разделе комментариев этого вопроса .Есть также другие проблемы правдоподобия с вопросом, который вы можете найти в разделе комментариев.Имейте в виду, что некоторые вопросы о стековом потоке задают люди, которые точно не знают, какие шаги они предпринимают при возникновении проблем .Я бы не стал слишком беспокоиться об этом конкретном вопросе;)

...