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

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

16.09.2016, 12:05. Показов 5605. Ответов 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
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,818
17.09.2016, 00:17
Цитата Сообщение от Kastaneda Посмотреть сообщение
Вопрос - как компилятора без линкера разруливает это дело, ведь constexpr value ему нужен на этапе компиляции? Если он это пережевывает, почему тогда constexpr необходим?
Если понадобится адрес переменной, то потребуется определение (без него будет ошибка линкера). До этих пор линкер вообще не задействуется - это compile-time константа.
1
 Аватар для Kastaneda
5232 / 3206 / 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
19500 / 10105 / 2461
Регистрация: 30.01.2014
Сообщений: 17,818
17.09.2016, 18:16
Цитата Сообщение от Kastaneda Посмотреть сообщение
как компилятор обходится без линкера?
Точно также, как он обходится без него, когда ты передаешь адрес функции в параметр шаблона.
1
 Аватар для Kastaneda
5232 / 3206 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
18.09.2016, 08:45  [ТС]
Цитата Сообщение от ct0r Посмотреть сообщение
Эта штука называется address constant expression
Во, спасибо, знаний вот этого термина мне не хватало.

Цитата Сообщение от DrOffset Посмотреть сообщение
Точно также, как он обходится без него, когда ты передаешь адрес функции в параметр шаблона.
Да, логично, похоже разобрались
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13183 / 6819 / 1821
Регистрация: 18.10.2014
Сообщений: 17,260
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
13183 / 6819 / 1821
Регистрация: 18.10.2014
Сообщений: 17,260
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
13183 / 6819 / 1821
Регистрация: 18.10.2014
Сообщений: 17,260
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 / 3206 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
22.09.2016, 06:54  [ТС]
После этого
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Фактически, все, чего вы добиваетесь указанием constexpr в этом объявлении указателя, это превращение в constexpr таких выражений, как PTR[5]. Для этого инициализатор PTR должен быть виден компилятору, т.е. присутствовать в объявлении. Ясно, что для определения значения PTR[5] в таком случае компилятору совсем не нужно знать конкретного значения PTR
окончательно все встало на свои места, спасибо.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13183 / 6819 / 1821
Регистрация: 18.10.2014
Сообщений: 17,260
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
Ответ Создать тему
Новые блоги и статьи
Доступность команды формы по условию
Maks 07.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: сделать доступной кнопку (команда формы "ЗавершитьСписание") при. . .
Уведомление о неверно выбранном значении справочника
Maks 06.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "НарядПутевка", разработанного в конфигурации КА2. Задача: уведомлять пользователя, если в документе выбран неверный склад. . .
Установка Qt Creator для C и C++: ставим среду, CMake и MinGW без фреймворка Qt
8Observer8 05.04.2026
Среду разработки Qt Creator можно установить без фреймворка Qt. Есть отдельный репозиторий для этой среды: https:/ / github. com/ qt-creator/ qt-creator, где можно скачать установщик, на вкладке Releases:. . .
AkelPad-скрипты, структуры, и немного лирики..
testuser2 05.04.2026
Такая программа, как AkelPad существует уже давно, и также давно существуют скрипты под нее. Тем не менее, прога живет, периодически что-то не спеша дополняется, улучшается. Что меня в первую очередь. . .
Отображение реквизитов в документе по условию и контроль их заполнения
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеСпецтехники", разработанного в конфигурации КА2. Данный документ берёт данные из другого нетипового документа. . .
Фото всей Земли с борта корабля Orion миссии Artemis II
kumehtar 04.04.2026
Это первое подобное фото сделанное человеком за 50 лет. Снимок называют новым вариантом легендарной фотографии «The Blue Marble» 1972 года, сделанной с борта корабля «Аполлон-17». Новое фото. . .
Вывод диалогового окна перед закрытием, если документ не проведён
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать программный контроль на предмет проведения документа. . .
Программный контроль заполнения реквизитов табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: 1. Реализовать контроль заполнения реквизита. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru