Аргумент без типа шаблона, C ++ 11, ограничение для строковых литералов

Правила ограничений для аргументов, не относящихся к типу, гласят:

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

- для нетипового параметра-шаблона целочисленного или перечислимого типа преобразованное постоянное выражение (5.19) типа параметра-шаблона; или

- имя нетипового параметра-шаблона; или

- постоянное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешней или внутренней связью или функцию с внешней или внутренней связью, включая шаблоны функций и идентификаторы шаблонов функций, но исключая нестатические члены класса, выраженные ( игнорируя круглые скобки) как & id-expression, за исключением того, что & может быть опущено, если имя относится к функции или массиву, и должно быть опущено, если соответствующий параметр шаблона является ссылкой; или

- постоянное выражение, которое возвращает значение нулевого указателя (4.10); или

- константное выражение, которое оценивается как значение указателя на нулевой член (4.11); или

- указатель на член, выраженный, как описано в 5.3.1.

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

[ Example:
template<class T, const char* p> class X {
/ ... /
};
X<int, "Studebaker"> x1; // error: string literal as template-argument
const char p[] = "Vivisectionist";
X<int,p> x2; // OK
—end example ] —end note ]

Итак, почему строковый литерал не может использоваться в качестве аргумента для параметра, не являющегося типом?

const char arr[5] = "1234";

arr имеет тот же тип const char [5], что и

"1234"; 

arr имеет внешнюю связь, и поэтому до стандарта С ++ 11 разрешалось использовать arr в качестве аргумента шаблона, не являющегося типом.

Но теперь указатели на объекты с внутренней связью (статическое хранилище) также можно использовать в качестве аргументов шаблона, не являющихся типом, а строковый литерал имеет внутреннюю привязку.


person Vahagn Babajanyan    schedule 27.02.2015    source источник
comment
Было бы замечательно, если бы язык можно было расширить, чтобы можно было использовать строковые литералы при вызове вариативного template<char... c>. Думаю, это должно быть легко. И это бы обошло проблему увязки   -  person Aaron McDaid    schedule 11.07.2015


Ответы (2)


Ближе всего к разрешению строковые литералы подходят из вашего вопроса:

постоянное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешней или внутренней связью

Строковые литералы не имеют ни внешней, ни внутренней связи, поэтому они не разрешены.

Если у вас есть несколько единиц перевода, каждая из которых содержит определение const char arr[5]; с внутренней связью, тогда все они являются отдельными объектами с разными адресами, но всегда внутри одной единицы перевода arr == arr. Реализации выяснили, как заставить это работать для аргументов шаблона.

Если у вас несколько единиц перевода, каждая из которых содержит "1234", то не гарантированно имеют разные адреса. Однако даже в одной единице перевода у них не гарантированно будет один и тот же адрес.

Если "1234" != "1234", то ссылка на шаблон S<"1234"> не имеет смысла: каждый раз вы будете ссылаться на разные экземпляры шаблона.

Если "1234" == "1234", то реализациям усложняется обеспечение того, чтобы S<"1234"> был одного и того же типа в каждой единице перевода.

person Community    schedule 27.02.2015

Действительно интересно, что следующий код действителен для C ++ 11, но строковые литералы не работают. Для C ++ 14 вы даже можете удалить static_cast.

#include <iostream>

static constexpr const char s1[] = "Hello World!";
static const char s2[] = __DATE__ " " __TIME__;

template< const char* STR > struct X {X() {std::cout << STR << std::endl;}};
X< s1 > x1;
X< static_cast<const char*>(s2) > x2;

int main() {}

Даже если строковые литералы не принимаются современными компиляторами C ++, в некоторых случаях это было бы очень удобно.

Принятие адресов временных экземпляров классов в качестве аргумента шаблона NT приведет к аналогичному направлению:

#include <iostream>

struct S {int s;};
static constexpr const S s1{123};
static const S s2{999};

template< const S* _S > struct X {X() {std::cout << _S->s << std::endl;}};
X< &s1 > x1;
X< &s2 > x2;
//NOT OK: X< (&S{1}) > x3;

int main() {}
person wiRe    schedule 27.07.2019