Когда и как использовать шаблон буквального оператора?

На cppreference есть упоминание, что можно иметь шаблонные пользовательские операторы с некоторыми ограничениями:

Если литеральный оператор является шаблоном, он должен иметь пустой список параметров и может иметь только один параметр шаблона, который должен быть нетипизированным пакетом параметров шаблона с типом элемента. char, такие как

template <char...> double operator "" _x();

Итак, я написал один, как в коде ниже:

template <char...> 
double operator "" _x()
{
    return .42;
}

int main()
{
    10_x; // empty template list, how to specify non-empty template parameters?
}

Вопрос:

  1. Код работает, но как я могу использовать оператор с некоторыми непустыми параметрами шаблона? 10_x<'a'>; или же 10_<'a'>x; не компилируется.
  2. Есть ли у вас пример реального использования таких шаблонных операторов?

3 ответа

Решение
10_x; // empty template list, how to specify non-empty template parameters?

Это не совсем верно. Список параметров шаблона не пуст. Когда вы пишете:

template <char... Cs> 
??? operator "" _x()

Cs получить от материала на левой стороне литерала. То есть когда ты пишешь:

10_x

что вызывает:

operator ""_x<'1', '0'>();

Одним простым примером может быть построение двоичного литерала, безопасного для переполнения, во время компиляции, который бы:

template <uint64_t V>
constexpr uint64_t make_binary() {
    return V;
}

template <uint64_t V, char C, char... Cs>
constexpr uint64_t make_binary() {
    static_assert(C == '0' || C == '1', "invalid binary");

    return make_binary<2*V + C - '0', Cs...>();
}

template <char... Cs> 
uint64_t operator "" _b()
{
    static_assert(sizeof...(Cs) <= 64, "overflow");

    return make_binary<0, Cs...>();
}

uint64_t a = 101_b; // OK: a == 5
uint64_t b = 102_b; // error: invalid
uint64_t c = 11111111110000000000111111111100000000001111111111000000000011111111110000000000_b; // error: overflow

Параметры вашего шаблона уже определены - это символы исходного кода, составляющие ваше буквальное значение! Таким образом, для 10_xвы на самом деле звоните:

template<> double operator "" _x<'1', '0'>();

Вот рабочий пример. Он компилируется без ошибок, и ни одно из утверждений не срабатывает.

#include <cassert>

enum class MyEnum
{
  ONE,
  TWO,
  THREE
};

template<char...> MyEnum operator "" _e();

template<> MyEnum operator "" _e<'1'>()
{
  return MyEnum::ONE;
}
template<> MyEnum operator "" _e<'2'>()
{
  return MyEnum::TWO;
}
template<> MyEnum operator "" _e<'3'>()
{
  return MyEnum::THREE;
}

int main()
{
  assert(1_e == MyEnum::ONE);
  assert(2_e == MyEnum::TWO);
  assert(3_e == MyEnum::THREE);
}

Вы можете как-то разработать пакет параметров (как уже упоминалось другими) или получить к ним доступ в виде строки времени компиляции, если вы предпочитаете:

template<int N>
constexpr double f(const char(&str)[N]) { return .42; }

template <char... C> 
constexpr double operator "" _x()
{
     return f({C...});
}

Есть ли у вас пример реального использования таких шаблонных операторов?

Вы можете использовать вышеупомянутую технику, чтобы иметь дело с преобразователем строки во время компиляции и иметь что-то вроде 10_x вместо f("10") или что угодно.

Другие вопросы по тегам