Undefined ссылка на статический constexpr char []

Я хочу иметь массив static const char в моем классе. GCC пожаловался и сказал мне, что я должен использовать constexpr, хотя теперь он сказал мне ссылку undefined. Если я сделаю массив не-членом, тогда он скомпилируется. Что происходит?

// .hpp
struct foo {
  void bar();
  static constexpr char baz[] = "quz";
};

// .cpp
void foo::bar() {
  std::string str(baz); // undefined reference to baz
}
4b9b3361

Добавьте в файл cpp:

constexpr char foo::baz[];

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

117
ответ дан 05 нояб. '11 в 2:22
источник

В С++ 03 нам разрешалось предоставлять в классах intializers для константных интегралов или типов перечисления констант, в С++ 11 с использованием constexpr это было расширено до литералов.

В С++ 11 нам не нужно предоставлять определение области пространства имен для статического члена constexpr, если оно не используется odr, мы можем видеть это из стандартного раздела проекта С++ 11 9.4.2 [class. static.data], в котором говорится (акцент мой идет вперед):

[...] Статический член данных типа literal может быть объявлен в классе определение с помощью спецификатора constexpr; если это так, то его выражение должно задайте инициализатор скобок или равный, в котором каждый параметр-инициализатор то есть выражение-присваивание является постоянным выражением. [Примечание: в оба эти случая, член может появляться в постоянных выражениях. -конец заметка ] Член все еще должен быть определен в области пространства имен, если он используется в качестве параметра (3.2) в программе и определении области пространства имен не должен содержать инициализатор.

Итак, тогда вопрос будет, используется baz odr:

std::string str(baz); 

и ответ "да", поэтому нам также требуется определение области пространства имен.

Итак, как определить, используется ли переменная odr? В исходной формулировке С++ 11 в разделе 3.2 [basic.def.odr] говорится:

Выражение потенциально оценивается, если оно не является неоценимым операнд (пункт 5) или его подвыражение. Переменная, имя которой появляется как потенциально оцененное выражение используется odr, еслиэто объект, который удовлетворяет требованиям для появления в постоянное выражение (5.19) и преобразование lvalue-to-rvalue (4.1) немедленно применяется.

Итак, baz дает константное выражение, но преобразование lvalue-to-rvalue не применяется сразу, поскольку оно неприменимо, поскольку baz является массивом. Это описано в разделе 4.1 [conv.lval], в котором говорится:

Значение gl (3.10) нефункционного типа без массива T может быть преобразован в prvalue.53 [...]

Что применяется при преобразовании массива в указатель.

Эта формулировка [basic.def.odr] была изменена из-за Отчет о дефектах 712, поскольку некоторые случаи не были охвачены этой формулировкой, но эти изменения не изменяют результаты для этого случая.

39
ответ дан 04 марта '15 в 7:19
источник

Это действительно недостаток в С++ 11, как объяснили другие, в С++ 11 статическая переменная-член constexpr, в отличие от любой другой глобальной переменной constexpr, имеет внешнюю связь, поэтому должна быть явно определена где-то.

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

Хорошая новость: этот недостаток исправлен в С++ 17! Этот подход немного запутан, хотя: в С++ 17 статические константы-члены constexpr неявно встроены. inline применяется к переменным - это новая концепция в С++ 17, но она фактически означает, что они не нуждаются в явном определении в любом месте.

8
ответ дан 13 окт. '17 в 1:02
источник

Не более элегантное решение может изменить char[] на:

static constexpr char * baz = "quz";

Таким образом, мы можем иметь определение/декларация/инициализатор в 1 строке кода.

2
ответ дан 19 апр. '16 в 17:40
источник