Ответ 1
1. Короткий ответ: он работает независимо от того, объявляется ли он constexpr
, потому что вы определяете объект со статической продолжительностью хранения (это не строковый литерал - он хранит копию содержимое одного), а его адрес является постоянным выражением. Что касается связи, str2
имеет внутреннюю связь, но это прекрасно - его адрес может использоваться как аргумент шаблона непигового типа.
Длинный ответ:
В С++ 11 и 14, [14.3.2p1] говорит следующее:
Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из:
[...]
- постоянное выражение (5.19), которое обозначает адрес полного объекта со статическим временем хранения и внешним или внутренним связь или функция с внешней или внутренней связью, включая шаблонов функций и шаблонов-шаблонов функций, но исключая нестатические члены класса, выраженные (игнорируя круглые скобки) как
&
id-expression, где id-выражение - это имя объекта или функции, кроме что&
может быть опущен, если имя относится к функции или массиву и должен быть опущен, если соответствующий шаблон-параметр является ссылка;[...]
Таким образом, вы можете использовать адрес объекта со статической продолжительностью хранения, но объект должен быть идентифицирован именем с привязкой (внутренним или внешним), а также тем, как вы выражаете этот адрес. (Строковые литералы не являются именами и не имеют привязки.)
Короче говоря, даже char str1[] = "Test 1";
работает. static char str1[] = "Test 1";
также прекрасен; GCC 5.1.0 отвергает его, но я думаю, что ошибка; Clang 3.6.0 принимает его.
О str2
linkage, С++ 11 и 14 [3.5p3] говорит:
Имя, имеющее область пространства имен (3.3.6), имеет внутреннюю связь, если это имя [...]
- неопределенная переменная, которая явно объявлена
const
илиconstexpr
, и не объявлено явноextern
, а не ранее заявлено, что имеет внешнюю связь;[...]
N4431 изменил это немного, в результате DR 1686, чтобы:
- переменная типа энергонезависимой конструкции, которая не объявлена явно
extern
и не объявлена ранее связь;
отражающий тот факт, что constexpr
подразумевает const-квалификацию для объектов.
2. Краткий ответ: для С++ 11 и 14 см. выше; для проекта С++ 1z, str3
не является постоянным выражением, поскольку сам указатель не является constexpr
, а также адресом строкового литерала. str4
является постоянным, но все равно адресом строкового литерала.
Длинный ответ:
В текущем рабочем черновике N4431 ограничения на аргументы шаблона не-типа были ослаблены. [14.3.2p1] теперь говорит:
Аргумент шаблона для параметра шаблона, не относящегося к типу, должен быть преобразованное постоянное выражение (5.20) типа Шаблон-параметры. Для шаблона-шаблона, не относящегося к типу ссылки или тип указателя, значение константного выражения не должно ссылаться на (или для типа указателя, не должен быть адресом):
- подобъект (1.8),
- временный объект (12.2),
- строковый литерал (2.13.5),
- результат выражения
typeid
(5.2.8) или- предопределенная переменная
__func__
(8.4.1).
И это все ограничения. Часть преобразованного константного выражения довольно важна; полное определение длинное, но одна часть, относящаяся к нашему случаю, такова, что адрес объекта со статической продолжительностью хранения является таким выражением.
Также важно, что согласно [5.20p2.7] преобразование lvalue-to-rale, примененное к
нестабильное значение glvalue, которое относится к неопределенному объекту с
constexpr
, или это относится к не изменяемому под-объекту такого объект
также удовлетворяет условиям постоянного выражения. Это позволяет нам использовать некоторые переменные-указатели constexpr
в качестве аргументов шаблона непигового типа. (Обратите внимание, что просто объявить переменную const
недостаточно, так как она может быть инициализирована не константным выражением.)
Итак, что-то вроде constexpr const char* str3 = str1;
отлично. Он принят Clang 3.6.0 в режиме С++ 1z (и отклонен в режиме С++ 14); GCC 5.1.0 все еще отклоняет его - похоже, что он еще не реализовал обновленные правила.
И все-таки, что случилось со строковыми литералами? Здесь проблема (N4431 [2.13.5p16]):
Оценка строкового литерала приводит к строковому литералу с статическая продолжительность хранения, инициализированная от заданных символов как указанных выше. Все ли струнные литералы различны (то есть, хранятся в объектах с неперекрывающимися объектами) и оценки строкового литерала дают тот же или другой объект не указано.
Реализации разрешено делать много вещей со строковыми литералами: смешивать, сопоставлять, перекрывать (полностью или частично), делать 7 копий из одной и той же единицы перевода - что угодно. Это делает адрес строкового литерала непригодным для использования в качестве аргумента шаблона непигового типа.