Некоторые символы 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 16

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

шаблонные нетиповые аргументы

Шаблонный аргумент для параметра шаблона, не являющегося типом, должен быть одним из:

  1. для шаблона без типа-параметр интегрального или перечислительного типа, преобразованное постоянное выражение (5.19) типа template-параметр; или
  2. имя шаблона-параметра, не являющегося типом; или
  3. постоянное выражение (5.19), обозначающее адрес организации объект со статической длительностью хранения и внешней или внутренней связью или функция с внешней или внутренней связью, включая функцию шаблоны и функции template-id, но исключая нестатический класс члены, выраженные (игнорируя скобки) как & id-выражение, за исключением что & может быть опущено, если имя ссылается на функцию или массив и должен быть опущен, если соответствующий шаблон-параметр является ссылка; или
  4. постоянное выражение, которое возвращает нулевое значение указателя (4.10); или
  5. постоянное выражение, которое вычисляет значение указателя нулевого элемента (4.11); или
  6. указатель на член, выраженный как описано в 5.3.1; или
  7. постоянное выражение адреса типа std:: nullptr_t.

На ваши вопросы:

Почему namespace_const_message и const_message недоступны во время компиляции и поэтому запрещены в функции шаблона печати?

Это почему constexpr существует. Они могут быть использованы там, где это необходимо оценка времени компиляции, таким образом, доступны в качестве аргументов шаблона.

верно ли мое предположение о строковых литералах?

Об этом есть заметка сразу после аргументов:

Примечание: строковый литерал (2.14.5) не удовлетворяет требованиям ни одной из этих категорий и, следовательно, не является приемлемый шаблон-аргумент.