Пользовательские литеральные аргументы не являются constexpr? - PullRequest
23 голосов
/ 13 ноября 2011

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

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

Я смущен этим - разве литералы не являются константными выражениями?5 в 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
}

Ответы [ 5 ]

11 голосов
/ 28 января 2017

Я не знаю, есть ли лучший способ сделать это в C ++ 11, чем текущий принятый ответ, но с ослабленным constexpr в C ++ 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, "!");
4 голосов
/ 13 ноября 2011

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

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!

4 голосов
/ 13 ноября 2011

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

3 голосов
/ 13 ноября 2011

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

1 голос
/ 13 ноября 2011

@ Pubby.Самый простой способ переварить пакет параметров типа 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.

...