Constexpr приведено к const char[]
я решил принять, по-видимому, непригодный подход: использование template <char...>
классы.
это то, что я придумал, это очень распространено, ничего особенного, а также не работает.
template <char... chars> class string
{
public:
static constexpr const char value[] = {chars...};
constexpr string()
{
}
constexpr operator decltype(value) & () const
{
return value;
}
};
template <char... chars> constexpr const char string <chars...> :: value[];
моя идея была сделать string
экземпляр constexpr
constructible и подвергая какой-то constexpr кастинг, чтобы он обеспечивал его содержание.
теперь, если я это сделаю
static constexpr const char x[] = "ciao";
template <const char * str> void print()
{
std :: cout << str << std :: endl;
}
print <x> ();
это работает и говорит ciao
. Я получаю ciao
также если я делаю
std :: cout << string <'c', 'i', 'a', 'o'> {} << std :: endl;
или
print <string <'c', 'i', 'a', 'o', ''> :: value> ();
но когда я делаю
print <string <'c', 'i', 'a', 'o', ''> {}> ();
я: No matching function for call to print
.
Я определенно что-то упускаю. Неужели невозможно сделать то, что я пытаюсь сделать? Заставляя экземпляр выполнять приведение constexpr, чтобы как-то вернуть value
? Если бы это сработало, я смог бы легко сделать операторы и строку манипуляция во время компиляции, "единственным" недостатком является ультра-скучный 'i', 'n', 'i', 't', 'i', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n'
.
дальнейшие эксперименты
я сделал еще один эксперимент, который отлично работает.
template <char... chars> class string
{
public:
constexpr string()
{
}
constexpr operator size_t () const
{
return sizeof...(chars);
}
};
template <size_t length> void print()
{
std :: cout << length << std :: endl;
}
print <string <'c', 'i', 'a', 'o'> {}> ();
и он печатает довольно 4
.
2 ответов
в C++14 ограничение аргументов шаблона параметром не-типа шаблона:
A шаблон-аргумент для не-типа, не-шаблона шаблон-параметр будет одним из:
* для не-типа шаблон-параметр интегрального или перечислительного типа, преобразованное постоянное выражение (5.19) типа шаблон-параметр; или
* название типа шаблон-параметр; или
* постоянное выражение (5.19), которое обозначает адрес полного объекта со статической длительностью хранения и внешней или внутренней связью или функцию с внешней или внутренней связью, включая шаблоны функций и функцию template-ids но исключая нестатические члены класса, выраженные (игнорируя круглые скобки) как&
id-выражение, где id-выражение имя объект или функция, за исключением того, что&
может быть опущено, если имя относится к функции или массиву и должно быть опущено, если соответствующее шаблон-параметр - это ссылка; или
* постоянное выражение, которое вычисляет значение нулевого указателя (4.10); или
* постоянное выражение, которое вычисляет значение указателя нулевого члена (4.11); или
* указатель на элемент, выраженный, как описано в разделе 5.3.1; или
* постоянное выражение типаstd::nullptr_t
.
в вашем примере string<'c', 'i', 'a', 'o', ''>{}
ничего из этого. Обратите внимание, что первый маркер ограничен интегральным или перечислительным типом и const char*
не является ни тем, ни другим (исключение для интегральных типов-это то, что позволяет компилировать ваш пример "дальнейших экспериментов"). Другие пули не действуют. Итак, соответствующий C++14 должны отклонить ваш код.
обходной путь-просто передать базовый массив символов напрямую:
print<string<'c', 'i', 'a', 'o', ''>::value>();
или взять сам тип в качестве шаблона тип'>>();
или... вы будете рады узнать, что в C++17, все наладится. См.N4198, с мотивирующим примером: в документе было предложено меньше ограничений на аргументы не-типа шаблона, что сделало бы приведенный выше пример правильно построенный. Новая формулировка гласит: A шаблон-аргумент для не-типа шаблон-параметр должно быть преобразованным постоянным выражением (5.20) типа шаблона-параметра. Для типа шаблон-параметр ссылочного типа или типа указателя значение постоянного выражения не должно ссылаться на (или для типа указателя не должно быть адресом): вот и все. Все остальное разрешено. С
template<int *p> struct A {};
int n;
A<&n> a; // ok
constexpr int *p() { return &n; }
A<p()> b; // error
* подобъект (1.8),
* времянка объекта (12.2),
* строковый литерал (2.14.5),
* результат a typeid
выражение (5.2.8), или
* предопределенное __func__
переменной (8.4.1).string<'c', 'i', 'a', 'o', ''>{}
преобразуется в указатель, который не является ни одной из этих вещей, пример становится хорошо сформированным. Clang 3.8 скомпилирует ваш пример в режиме c++1z (правильно), но не в режиме c++14 (правильно). GCC пока не реализует это. Просто дай им время.
пользовательские преобразования не рассматриваются для сопоставления аргументов шаблона.
впрочем, вместо
print <string <'c', 'i', 'a', 'o', ''>{} > ();
... вы можете просто написать
print <string <'c', 'i', 'a', 'o', ''>::value> ();
или вы можете определить
template <const char* str>
void print()
{
std::cout << str << std::endl;
}
template< class Type >
void print()
{
print<Type::value>();
}
... а потом пиши просто
print <x> ();
print <string <'c', 'i', 'a', 'o', ''>> ();
... где x
ваша область пространства имен
static constexpr const char x[] = "ciao";
Примечание: этот код компилируется с MinGW g++ 5.1.0 с -std=c++14
, но не с обновлением Visual C++ 2015 2. Такие различия общим для кода, который раздвигает границы языка. Стандарт находится в движении, и компиляторы развиваются, чтобы попытаться идти в ногу со стандартом, и поэтому такой код на практике не очень переносим.