Подтвердить что ты не робот

Определенные пользователем литералы не являются constexpr?

Я тестирую пользовательские литералы. Я хочу сделать _fac возврат факториала числа.

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

Я смущен этим - не являются ли литералы постоянными выражениями? 5 in 5_fac всегда является литералом, который можно оценить во время компиляции, поэтому почему я не могу использовать его как таковой?

Первый метод:

constexpr int factorial_function(int x) {
  return (x > 0) ? x * factorial_function(x - 1) : 1;
}

constexpr int operator "" _fac(unsigned long long x) {
  return factorial_function(x); // this works
}

Второй метод:

template <int N> struct factorial_template {
  static const unsigned int value = N * factorial_template<N - 1>::value;
};
template <> struct factorial_template<0> {
  static const unsigned int value = 1;
};

constexpr int operator "" _fac(unsigned long long x) {
  return factorial_template<x>::value; // doesn't work - x is not a constexpr
}
4b9b3361

Ответ 1

Вот как я это сделал:

template <typename t>
constexpr t pow(t base, int exp) {
  return (exp > 0) ? base * pow(base, exp-1) : 1;
};

template <char...> struct literal;
template <> struct literal<> {
  static const unsigned int to_int = 0;
};
template <char c, char ...cv> struct literal<c, cv...> {
  static const unsigned int to_int = (c - '0') * pow(10, sizeof...(cv)) + literal<cv...>::to_int;
};

template <int N> struct factorial {
  static const unsigned int value = N * factorial<N - 1>::value;
};
template <> struct factorial<0> {
  static const unsigned int value = 1;
};

template <char ...cv>
constexpr unsigned int operator "" _fac()
{
  return factorial<literal<cv...>::to_int>::value;
}

Огромное спасибо KerrekSB!

Ответ 2

Я не знаю, есть ли лучший способ в С++ 11 сделать это, чем текущий принятый ответ, но с расслабленным constexpr в С++ 14 вы можете просто написать "обычный" код:

constexpr unsigned long long int operator "" _fac(unsigned long long int x) {
    unsigned long long int result = 1;
    for (; x >= 2; --x) {
        result *= x;
    }
    return result;
}

static_assert(5_fac == 120, "!");

Ответ 3

Чтобы использовать constexpr с определенными пользователем литералами, вам, видимо, придется использовать вариационный шаблон. Взгляните на второй список в статье в википедии для примера.

Ответ 4

Я могу ошибаться, но я думаю, что функции constexpr также могут вызываться с непостоянными аргументами (в этом случае они не дают постоянного выражения и оцениваются во время выполнения). Что бы не сработало с аргументами шаблона не-типа.

Ответ 5

@Pubby. Легкий способ переварить пакет параметров char non-type - это закрепить его в списке инициализаторов для строки. Затем вы можете использовать atoi, atof и т.д.:

#include <iostream>

template<char... Chars>
  int
  operator "" _suffix()
  {
    const char str[]{Chars..., '\0'};
    return atoi(str);
  }

int
main()
{
  std::cout << 12345_suffix << std::endl;
}

Не забудьте применить нулевой символ для функций стиля C.