Дело в том, что указанный слева квалификатор
const относится к правой половине спецификации типа до первого модификатора.
const char& - ссылка на константный символ. Квалификатор -
const, модификатор -
&.
const char* - указатель на память константного символа. Квалификатор -
const, модификатор -
*.
При этом, указанный справа квалификатор
const относится ко всей части спецификации типа левее, включая все модификаторы.
char* const - константный указатель на память символа.
const char* const - константный указатель на память константного символа.
char* const * - указатель на память константного указателя на память символа.
char& const существовать не может, т.к. квалификаторы не применяется к ссылкам. Тут будет ошибка трансляции.
И при чем же здесь
constexpr? Просто
constexpr всегда относится только ко всей спецификации типа со всеми модификаторами.
const char* - указатель на память константного символа.
constexpr char* - константный указатель времени компиляции на память символа. Тут нет ошибки, память символа тут считается модифицируемой.
И если объект с типом
constexpr char* получит характеристику ODR-used [
?], то после трансляции кода это будет уже объект с типом
char* const. Вот так.
В то же время, строковые литералы имеют тип
const char[N], т.е. статически определенный массив константных символов. Такой тип можно привести только к типу
const char*.
В результате, чтобы правильно определить константный указатель времени компиляции на память константного символа, нужно тип определить как
constexpr const char*.
И
const в этом месте никакого прямого отношения к
constexpr не имеет.