Я хочу иметь static const
char
массив в моем классе. GCC пожаловался и сказал мне, что я должен использовать constexpr
, хотя теперь он говорит мне, что это неопределенная ссылка. Если я сделаю массив не членом, он будет скомпилирован. Что происходит?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
c++
c++11
static-members
constexpr
Pubby
источник
источник
int
@MooingDuck. Он отлично работает как не член. Не нарушит ли это тоже правило?int
с чит. Как неучастник, это не должно быть разрешено, если правила не изменены для C ++ 11 (возможно)Ответы:
Добавьте в ваш файл cpp:
Причина: Вы должны предоставить определение статического члена, а также объявление. Объявление и инициализатор находятся внутри определения класса, но определение члена должно быть отдельным.
источник
decltype(foo::baz) constexpr foo::baz;
C ++ 17 вводит встроенные переменные
C ++ 17 исправляет эту проблему для
constexpr static
переменных-членов, требующих внепланового определения, если оно было использовано odr. См. Вторую половину этого ответа для деталей до C ++ 17.Предложение P0386 Inline Variables предоставляет возможность применять
inline
спецификатор к переменным. В частности, к этому случаюconstexpr
относятсяinline
статические переменные-члены. Предложение говорит:и модифицировал [basic.def] p2:
и добавьте [depr.static_constexpr] :
C ++ 14 и более ранние
В C ++ 03 нам было разрешено предоставлять только инициализаторы в классе для константных интегралов или константных типов перечисления , в C ++ 11 использование
constexpr
этого было распространено на литеральные типы .В C ++ 11 нам не нужно предоставлять определение области имен для статического
constexpr
члена, если он не используется odr , это можно увидеть из черновика стандартного раздела C ++ 119.4.2
[class.static.data], в котором говорится ( акцент мой идет вперед ):Итак, возникает вопрос,
baz
используется ли здесь odr :и ответ - да , и поэтому нам также требуется определение области имен.
Так как же определить, используется ли переменная odr ? Оригинальная формулировка C ++ 11 в разделе
3.2
[basic.def.odr] гласит:Таким
baz
образом, получается константное выражение, но преобразование lvalue-to-rvalue применяется не сразу, так как оно неприменимо из-за того,baz
что является массивом. Это4.1
описано в разделе [conv.lval], где говорится:Что применяется при преобразовании массива в указатель .
Эта формулировка [basic.def.odr] была изменена из-за Отчета о дефектах 712, поскольку некоторые случаи не были охвачены этой формулировкой, но эти изменения не изменяют результаты для этого случая.
источник
constexpr
имеет к этому никакого отношения? (baz
в любом случае это константное выражение)integral or enumeration type
но в противном случае, да, важно то, что это константное выражение .Это действительно недостаток в C ++ 11 - как объяснили другие, в C ++ 11 статическая переменная-член constexpr, в отличие от любого другого типа глобальной переменной constexpr, имеет внешнюю связь, поэтому должна где-то явно определяться.
Стоит также отметить, что на практике вы часто можете обходиться без статических переменных-членов constexpr без определений при компиляции с оптимизацией, поскольку они могут в конечном итоге быть встроенными во всех случаях, но если вы компилируете без оптимизации, часто ваша программа не будет связываться. Это делает это очень распространенной скрытой ловушкой - ваша программа прекрасно компилируется с оптимизацией, но, как только вы выключаете оптимизацию (возможно, для отладки), она не может установить связь.
Хорошая новость - этот недостаток исправлен в C ++ 17! Однако этот подход немного запутан: в C ++ 17 статические переменные-члены constexpr неявно встроены . Применение inline к переменным является новой концепцией в C ++ 17, но это фактически означает, что им нигде не требуется явное определение.
источник
Разве более элегантное решение не превращается
char[]
в:Таким образом, мы можем получить определение / объявление / инициализатор в 1 строке кода.
источник
char[]
вы можете использовать,sizeof
чтобы получить длину строки во время компиляции, сchar *
вы не можете (она вернет ширину типа указателя, в данном случае 1).sizeof
проблему и может быть использован в решениях «только для заголовка»Мой обходной путь для внешней компоновки статических членов заключается в использовании
constexpr
методов получения ссылочных членов (что не приводит к проблеме @gnzlbg, возникшей в качестве комментария к ответу @deddebme).Эта идиома важна для меня, потому что я ненавижу иметь несколько .cpp файлов в своих проектах и пытаюсь ограничить число до одного, который состоит только из
#include
s иmain()
функции.источник
В моем окружении gcc vesion - 5.4.0. Добавление -O2 может исправить эту ошибку компиляции. Кажется, gcc может справиться с этим случаем при запросе оптимизации.
источник