Стандарт С++: переменные constexpr с пространством имен имеют внутреннюю связь?
Представьте, что у нас есть заголовок foo.h
, содержащий следующее:
#ifndef FOO_H_
#define FOO_H_
namespace foo {
constexpr std::string_view kSomeString = "blah";
}
#endif // FOO_H_
Является foo::kSomeString
гарантированным наличие внутренней связи в любой единицы перевода, которая включает foo.h
? Это зависит от С++ 11 и С++ 17?
В черновом проекте [basic.link]/3 говорится
Имя, имеющее область пространства имен, имеет внутреннюю привязку, если это имя [...] не-встроенная переменная типа энергонезависимого типа с константой, которая не является явно объявленным extern или ранее объявлена как имеющая внешнюю связь [...]
Но я не знаю, считается ли constexpr
как "const-qualified". Говорит ли стандарт где-то?
Предполагая, что у этого гарантируется внутренняя связь, похоже, что с ODR не может быть проблем с этим использованием, верно? (В отличие от того, что он говорит в этом ответе.)
Ответы
Ответ 1
Да, constexpr
в объявлении объекта означает, что объект const
. См. [dcl.constexpr]/9. И да, это означает, что kSomeString
в вашем примере имеет внутреннюю привязку.
Вид нарушения ODR, о котором мы говорим, здесь не является определением самого kSomeString
, а другими определениями, которые пытаются его использовать. И есть проблема именно из-за внутренней связи. Рассмотрим:
void f(const std::string_view &);
inline void g() {
f(foo::kSomeString);
}
Это нарушение ODR, если оно включено в несколько единиц перевода, по существу потому, что определение g
в каждой единицы перевода ссылается на другой объект.
Ответ 2
Ваше использование kSomeString
совершенно правильно.
Обо всем по порядку; ваш объект является const-квалифицированным, как T.C. объяснил. Кроме того, он литерального типа, потому что он имеет конструктор constexpr. Поэтому, если вы используете его в функции, определенной в заголовочном файле, в стандарте применяется следующее исключение (глава 3.2):
Может быть более одного определения встроенной функции с внешняя связь (7.1.2), шаблон нестатической функции (14.5.6), (…) в программе при условии, что каждое определение появляется в другом перевод, и при условии, что определения удовлетворяют следующим требования. Учитывая, что такой объект с именем D определен более чем в одном блок перевода, затем
- каждое определение D должно состоять из одинаковой последовательности токенов; и
- в каждом определении D соответствующие имена, рассматриваемые в соответствии с 3.4, должны относиться к объекту, определенному в определении D, или должен ссылаться на тот же объект после разрешения перегрузки (13.3) и после сопоставления частичной специализации шаблона (14.8.3), за исключением того, что имя может ссылаться на объект const с внутренним или без связь, если объект имеет одинаковый литеральный тип во всех определениях D, и объект инициализируется с помощью постоянного выражения (5.19), и значение (но не адрес) объекта используется, и объект имеет одинаковое значение во всех определениях D;
- (...)