Некоторые символы const * недоступны во время компиляции?
Предположим, что у нас есть шаблонная функция с параметром не-типа const char *
, как это:
template <const char * MESSAGE> void print() {
std::cout << MESSAGE << 'n';
}
Использование этого шаблона не будет проблемой, так как журнал MESSAGE
может быть выведен во время компиляции, поэтому следующие варианты использования являются законными:
namespace {
char namespace_message[] = "Anonymous Namespace Message";
constexpr char namespace_constexpr_message[] = "Anonymous Namespace Constexpr Message";
}
char message[] = "Message";
constexpr char constexpr_message[] = "Constexpr Message";
int main()
{
print<namespace_message>();
print<namespace_constexpr_message>();
print<message>();
print<constexpr_message>();
return 0;
}
Но те, что ниже, не являются (смотрите здесь):
namespace {
const char namespace_const_message[] = "Anonymous Namespace Const Message";
}
const char const_message[] = "Const Message";
int main()
{
print<namespace_const_message>();
print<const_message>();
print<"Literal">();
return 0;
}
Ошибки, порожденные приведенным выше кодом, следующие:
Значение '{anonymous}:: namespace_const_message ' не может использоваться в константе выражение
Я не понимаю, почему namespace_const_message
не может использоваться в постоянном выражении, в то время как namespace_message
является; если я должен поставить на то, что один из них не может быть использован в постоянном выражении, я поставлю на то, что нет постоянного, но это тот, который уже работает как постоянное выражение!
Примечание: '{anonymous}::namespace_const_message' не был объявлен 'constexpr'
namespace_message
не был объявлен как constexpr
и используется в постоянное выражение, а его значение выводится при время компиляции. Почему constexpr
необходимо, если выражение const
и не требуется, если нет-const?
То же самое касается значений за пределами анонимного пространства имен, я пытался заставить константу времени компиляции поместить значения во внутреннее пространство связей, но очевидно, что я потерпел неудачу.
Наконец, последняя ошибка:
'"Literal" 'не является допустимым шаблонным аргументом для типа' const char*', поскольку строковые литералы никогда не могут быть использованы в этом контекст
Таким образом, удивительно (по крайней мере, для меня это было сюрпризом) строковый литерал не может использоваться в качестве аргумента шаблона, но пока строка (ну, указатель на массив символов, заканчивающийся нулем) является значением времени компиляции, она может использоваться в качестве параметров шаблона без типов, поэтому: они доступны во время компиляции, пока "они являются lvalue" (но они уже lvalues!).
Я пытаюсь понять, почему строковый литерал никогда не может быть использован в этом контексте, и мое лучшее предположение состоит в том, что два строковых литерала с одинаковым содержимым не являются одинаковыми литералами (потому что указатель, указывающий на содержимое, может быть разным), в то время как два интегральных литерала одинаковы (они являются значением, а не указателем на значение).
Так в чем же тут вопрос?- Почему
namespace_const_message
иconst_message
недоступны во время компиляции и поэтому запрещены в функции шаблонаprint
? - это мое предположение о строковых литералах правильно?
Спасибо.
2 ответа:
Переменная экземпляра шаблона должна иметь внешний вид связь, и
const
была неявно внутренней связью. Так что тебе придется напишите:extern char const constMessage[] = "Const message";
(Другой альтернативой может быть статический член класса. Статические члены класса всегда имеют внешнюю связь.)
Случай строковых литералов в некотором смысле аналогичен: их тип
char const[]
. Но это еще хуже: экземпляры шаблонов (по крайней мере ранние) нуждаются в имени, а строковый литерал его не имеет. Даже более того, не определено, идентичны ли строковые литералы являются одним и тем же объектом или нет, поэтому в следующем:template <char const* m> struct Toto { char const* f() const; }; Toto <"titi"> t1; Toto <"titi"> t2;
Было бы не определено, имеют ли
t1
иt2
один и тот же тип или нет.
Из стандарта c++11 §14.3.2.1
шаблонные нетиповые аргументы
Шаблонный аргумент для параметра шаблона, не являющегося типом, должен быть одним из:
- для шаблона без типа-параметр интегрального или перечислительного типа, преобразованное постоянное выражение (5.19) типа template-параметр; или
- имя шаблона-параметра, не являющегося типом; или
- постоянное выражение (5.19), обозначающее адрес организации объект со статической длительностью хранения и внешней или внутренней связью или функция с внешней или внутренней связью, включая функцию шаблоны и функции template-id, но исключая нестатический класс члены, выраженные (игнорируя скобки) как & id-выражение, за исключением что & может быть опущено, если имя ссылается на функцию или массив и должен быть опущен, если соответствующий шаблон-параметр является ссылка; или
- постоянное выражение, которое возвращает нулевое значение указателя (4.10); или
- постоянное выражение, которое вычисляет значение указателя нулевого элемента (4.11); или
- указатель на член, выраженный как описано в 5.3.1; или
- постоянное выражение адреса типа std:: nullptr_t.
На ваши вопросы:
Почему namespace_const_message и const_message недоступны во время компиляции и поэтому запрещены в функции шаблона печати?
Это почему
constexpr
существует. Они могут быть использованы там, где это необходимо оценка времени компиляции, таким образом, доступны в качестве аргументов шаблона.верно ли мое предположение о строковых литералах?
Об этом есть заметка сразу после аргументов:
Примечание: строковый литерал (2.14.5) не удовлетворяет требованиям ни одной из этих категорий и, следовательно, не является приемлемый шаблон-аргумент.