Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.92/25: Рейтинг темы: голосов - 25, средняя оценка - 4.92
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2

Как компилятор обрабатывает член класса static constexpr const char*

16.09.2016, 12:05. Показов 5547. Ответов 14
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет!

Наткнулся на непонятный момент
C++
1
2
3
4
5
class Foo
{
public:
    static constexpr const char* PTR = "value";
};
без constexpr не компилируется.
Вопрос - как компилятора без линкера разруливает это дело, ведь constexpr value ему нужен на этапе компиляции? Если он это пережевывает, почему тогда constexpr необходим?
1
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
16.09.2016, 12:05
Ответы с готовыми решениями:

Строки: почему компилятор пишет , что переменная типа char несовместима с const char?
char pass; int password( int d) { int m; char p; cout << "Введите пароль для доступа к информации:"; cin >> p; ...

Запись в static const char[]
У меня есть строка, содержащая некий текст. std::string ex_text = "<h1>TextTextText</h1>"; Также у меня есть переменная, которая должна...

Static член класса
Всем привет! Подскажите, пожалуйста, почему так писать нельзя: В хедере объявляю статический метод: public: explicit...

14
What a waste!
 Аватар для gray_fox
1610 / 1302 / 180
Регистрация: 21.04.2012
Сообщений: 2,733
16.09.2016, 22:28
Kastaneda, судя по сообщениям компилятора, const char* - не "интегральный" тип, и видимо в этом загвоздка - совмещать объявление статического члена класса и его инициализацию можно только для "интегральных" типов; в то время как при использовании constexpr инициализация необходима при объявлении.
1
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
17.09.2016, 00:17
Цитата Сообщение от Kastaneda Посмотреть сообщение
Вопрос - как компилятора без линкера разруливает это дело, ведь constexpr value ему нужен на этапе компиляции? Если он это пережевывает, почему тогда constexpr необходим?
Если понадобится адрес переменной, то потребуется определение (без него будет ошибка линкера). До этих пор линкер вообще не задействуется - это compile-time константа.
1
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
17.09.2016, 05:25  [ТС]
Цитата Сообщение от DrOffset Посмотреть сообщение
До этих пор линкер вообще не задействуется - это compile-time константа.
да, это я понимаю. Но ведь эта константа содержит адрес строки, который становится известным на этапе линковки, как компилятор обходится без линкера?
0
Игогошка!
 Аватар для ct0r
1801 / 708 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
17.09.2016, 14:33
Цитата Сообщение от Kastaneda Посмотреть сообщение
Но ведь эта константа содержит адрес строки, который становится известным на этапе линковки, как компилятор обходится без линкера?
1. Эта штука называется address constant expression.
2. А зачем ему линкер? Для таких объектов у компилятора адрес - это просто какая-то релокация. Он ее потом отправит в elf и все. А линкер дальше будет с ее использованием по соответствующему выражению адрес считать. Компилятору же нужно только знать, что эта релокация - константа.
3
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
17.09.2016, 18:16
Цитата Сообщение от Kastaneda Посмотреть сообщение
как компилятор обходится без линкера?
Точно также, как он обходится без него, когда ты передаешь адрес функции в параметр шаблона.
1
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
18.09.2016, 08:45  [ТС]
Цитата Сообщение от ct0r Посмотреть сообщение
Эта штука называется address constant expression
Во, спасибо, знаний вот этого термина мне не хватало.

Цитата Сообщение от DrOffset Посмотреть сообщение
Точно также, как он обходится без него, когда ты передаешь адрес функции в параметр шаблона.
Да, логично, похоже разобрались
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12936 / 6803 / 1821
Регистрация: 18.10.2014
Сообщений: 17,214
20.09.2016, 06:14
Лучший ответ Сообщение было отмечено HighPredator как решение

Решение

Цитата Сообщение от Kastaneda Посмотреть сообщение
Вопрос - как компилятора без линкера разруливает это дело, ведь constexpr value ему нужен на этапе компиляции? Если он это пережевывает, почему тогда constexpr необходим?
Концепция адресной константы была еще в языке С с самого начала стандартизованных времен. Про этом язык С никогда не поддерживал динамической инициализации для объектов со статическим storage duration. Т.е. при старте программы на C все объекты со статическим storage duration должны "родиться" уже проинициализированными, в том числе такие как

C
1
2
static int i;
static int *pi = &i;
Понятно, что значение pi не может быть известным на стадии компиляции. Более того, это значение даже в те времена в Unix не обязательно было известно даже на стадии линковки, а могло оказаться известным только на стадии загрузки (например, для глобальных объектов, определенных в shared objects). То есть конкретное значение адресной константы в общем случае может быть даже не линкером, а только загрузчиком в момент начала выполнения.

По этой причине спецификация адресных констант в С была специально подогнана под возможности загрузчиков. А именно, в С адресные константы выступают как константы только в инициализаторах и только либо сами собой, либо с прибавлением к адресной константе (или вычитанием из нее) какого-то смещения, известного на стадии компиляции (что разрешает в т.ч. применение операторов [] и -> в комбинации с & для формирования новых адресных констант). Другими словами, адресные константы в С могут быть использованы как константы только для целей относительной адресации.

То есть в рамках спецификации, нельзя написать

C
1
static int array[(size_t) pi % 100];
ибо, понятое дело, это требует знания точного значения (size_t) pi на стадии компиляции.

Так вот спецификация constexpr в С++ совсем не далеко уходит от этих ограничений, пришедших в С++ из С. Фактически, все, чего вы добиваетесь указанием constexpr в этом объявлении указателя, это превращение в constexpr таких выражений, как PTR[5]. Для этого инициализатор PTR должен быть виден компилятору, т.е. присутствовать в объявлении. Ясно, что для определения значения PTR[5] в таком случае компилятору совсем не нужно знать конкретного значения PTR.

Любые же попытки "выковырять" из PTR его численное значение на стадии компиляции (или как-то еще завязаться на абсолютную точку в памяти, куда он указывает) обречены на провал, даже несмотря на то, что PTR объявлен как constexpr.
7
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
20.09.2016, 09:26
Если вернуться к изначальному вопросу. Почему без constexpr не компилируется - уже ответили: инициализатор должен быть не в теле класса, а там, где объявлено само тело статического поля. Т.е. где-то вне тела класса должно быть написано

C++
const char* Foo::PTR = "value";
Из объяснений TheCalligrapher я так понял, что для статической инициализации указателя constexpr ничего принципиально нового не вносит (во всяком случае на низком уровне). А почему наличие constexpr позволяет инициализатор писать в теле класса? На всякий случай: мне непонятна только языковая часть, как работает компилятор, линкер, загрузчик и прочее на низком уровне, мне хорошо известно
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12936 / 6803 / 1821
Регистрация: 18.10.2014
Сообщений: 17,214
20.09.2016, 09:46
Цитата Сообщение от Evg Посмотреть сообщение
А почему наличие constexpr позволяет инициализатор писать в теле класса?
Для всех static constexpr разрешается писать инициализатор прямо в теле класса. Это необходимо для того, чтобы реализовать вычисления на стадии компиляции. В вычислениях на стадии компиляции и заключается вся идея constexpr.

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

При этом определением указателя PTR такое объявление не становится, даже несмотря на наличие инициализатора. Если где-то в коде PTR окажется использованным как lvalue, то придется его еще где-то определить. Инициализатор в определении повторять не надо.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
20.09.2016, 15:24
Я правильно понимаю ситуацию? Сначала (условно в C++98) разрешили писать инициализатор в теле класса ради того, чтобы static-поле класса можно было, грубо говоря, использовать в качестве размерности НЕавтоматического массива. Для указателей подобной необходимости не было, а потому не разрешали. Теперь (с C++11) из-за constexpr появилась необходимость использовать указатель (как rvalue) в других constexpr выражениях в НЕавтоматических местах, растущих от других constexpr'ов. Типа того?
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12936 / 6803 / 1821
Регистрация: 18.10.2014
Сообщений: 17,214
21.09.2016, 08:34
Лучший ответ Сообщение было отмечено HighPredator как решение

Решение

Цитата Сообщение от Evg Посмотреть сообщение
Я правильно понимаю ситуацию? Сначала (условно в C++98) разрешили писать инициализатор в теле класса ради того, чтобы static-поле класса можно было, грубо говоря, использовать в качестве размерности НЕавтоматического массива. Для указателей подобной необходимости не было, а потому не разрешали. Теперь (с C++11) из-за constexpr появилась необходимость использовать указатель (как rvalue) в других constexpr выражениях в НЕавтоматических местах, растущих от других constexpr'ов. Типа того?
Ну да. Типа того.

В С++98 (и С++03) возможность использования static const членов класса в качестве констант времени компиляции понимали (отсюда разрешение на указание инициализаторов для целых и enum типов), но это возможность все равно рассматривалась, как "вторичная". Например, правила ODR в С++98 формально требовали отдельного определения такого члена класса, если он использовался (used) в программе. При этом не придавалось никакого значения тому, как он использовался. Даже если static const использовался только как rvalue (напр. только для задания размера массива), все равно наличие определения безусловно требовалось спецификацией языка. Хотя понятно, что с точки зрения здравого смысла такое определение было никому не нужно. (Многие компиляторы натуральным образом не следили за соблюдением этого требования, если такая константа использовалась только как rvalue).

В С++11 возможность использования таких констант именно и только как констант времени компиляции уже вышла на первый план. Правила ODR были модифицированы и требуют отдельного определения static const членов класса только если они odr-используются (odr-used) в программе, т.е. если они где-то используются именно как lvalue. А если такая константа используется только как rvalue, то делать для нее отдельное определение больше не требуется.

Но дальше расширять функциональность static const не стали (возможно, там скрыты какие-то конфликты), т.е. указывать инициализаторы для static const прямо в классе по-прежнему можно только для целых и enum типов. Вместо этого для создания констант времени компиляции ввели совершенно новый механизм constexpr. Для constexpr констант указывать инициализаторы прямо в классе можно всегда, независимо от типа. Понятно, что наличие видимых отовсюду инициализаторов совершенно необходимо для того, чтобы обрабатывать такие константы на стадии компиляции.
3
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
21.09.2016, 16:12
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Даже если static const использовался только как rvalue (напр. только для задания размера массива), все равно наличие определения безусловно требовалось спецификацией языка. Хотя понятно, что с точки зрения здравого смысла такое определение было никому не нужно. (Многие компиляторы натуральным образом не следили за соблюдением этого требования, если такая константа использовалась только как rvalue)
Тут проблема немного другого уровня. Компилятор в общем-то не в состоянии проконтролировать, что определение неиспользуемой как value переменной где-то присутствует. Т.е. когда переменная используется (например, на неё взяли адрес), а определения нигде нет, то оно сломается на линковке. А когда переменная нигде не используется (все использования были заменены на константы), то очень сложно предпринимать усилия для того, чтобы сломаться в том случае, если определение переменной ни в одном из модулей не присутствует. Только путём построения избыточного кода или каких-то читерских вещей
0
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
22.09.2016, 06:54  [ТС]
После этого
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Фактически, все, чего вы добиваетесь указанием constexpr в этом объявлении указателя, это превращение в constexpr таких выражений, как PTR[5]. Для этого инициализатор PTR должен быть виден компилятору, т.е. присутствовать в объявлении. Ясно, что для определения значения PTR[5] в таком случае компилятору совсем не нужно знать конкретного значения PTR
окончательно все встало на свои места, спасибо.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12936 / 6803 / 1821
Регистрация: 18.10.2014
Сообщений: 17,214
22.09.2016, 22:19
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Если где-то в коде PTR окажется использованным как lvalue, то придется его еще где-то определить. Инициализатор в определении повторять не надо.
Тут, кстати, стоит добавить, что в С++17 все сильно меняется.

В С++17 вводится понятие inline variable. И можно будет писать так

C++
1
2
3
4
struct S
{
  inline static int x = 42;
}
при этом вышеприведенное объявление переменной S::x является ее определением. Переменную можно использовать как lvalue, и при этом какого-то дополнительного определения для нее делать не надо (и нельзя).

Далее, constexpr константы в С++17 становятся частным случаем inline variable. При этом, для сохранения обратной совместимости, писать отдельное "определение" для такой константы разрешается, но является deprecated

C++
1
2
3
4
5
6
struct S
{
  static constexpr int x = 42; // теперь это - определение
};
 
constexpr int S::x; // пока можно, но deprecated
5
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
22.09.2016, 22:19
Помогаю со студенческими работами здесь

Static-член класса
Подскажите пожалуйста в чем проблема, не получается инициализировать static-член указатель на объекты класса. counter инициализируется...

Можно ли инициализировать static член класса функцией?
Сабж. Конкретно - в классе есть static член типа SYSTEM_INFO (Windows.h), а мне нужно его инициализировать с помощью функции GetSystemInfo.

Инициализация в объявлении константных статических (const static) членов-данных класса.
Привет. Подскажите пожалуйста почему нельзя создавать в классе const static float, а const static int можно? class A{ const...

constexpr static functions
Почему нижеприведенный код не работает на gcc 4.7.1.? собственно как и на clang #include <iostream> class C1 { public:...

Ошибка сборки static constexpr int x[] = {.};
printf('Hello World!\n'); Хочу определить в классе статический массив. Пишу, к примеру: class Test { private: static...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru