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, с мотивирующим примером:

template<int *p> struct A {};
int n;
A<&n> a; // ok

constexpr int *p() { return &n; }
A<p()> b; // error

в документе было предложено меньше ограничений на аргументы не-типа шаблона, что сделало бы приведенный выше пример правильно построенный. Новая формулировка гласит:

A шаблон-аргумент для не-типа шаблон-параметр должно быть преобразованным постоянным выражением (5.20) типа шаблона-параметра. Для типа шаблон-параметр ссылочного типа или типа указателя значение постоянного выражения не должно ссылаться на (или для типа указателя не должно быть адресом):
* подобъект (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. Такие различия общим для кода, который раздвигает границы языка. Стандарт находится в движении, и компиляторы развиваются, чтобы попытаться идти в ногу со стандартом, и поэтому такой код на практике не очень переносим.