User defined literal arguments are not constexpr?
Asked Answered
L

5

23

I'm testing out user defined literals. I want to make _fac return the factorial of the number.

Having it call a constexpr function works, however it doesn't let me do it with templates as the compiler complains that the arguments are not and cannot be constexpr.

I'm confused by this - aren't literals constant expressions? The 5 in 5_fac is always a literal that can be evaluated during compile time, so why can't I use it as such?

First method:

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
}

Second method:

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
}
Leenaleeper answered 12/11, 2011 at 23:52 Comment(2)
I don't think any compiler implements those user-defined literals yet. How are you testing this?Perhaps
Nice! I didn't know that was supported yet. I have one of those installed on a machine somewhere, now I'm tempted to work out those variadic templates :-)Perhaps
L
5

This is how I ended up doing it:

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;
}

Huge thanks to KerrekSB!

Leenaleeper answered 13/11, 2011 at 0:49 Comment(1)
Downvoting, because the answer doesn't really answer the OPs stated questions, but only contains code.Holarctic
K
12

I don't know if there's a better way in C++11 to do this than the current accepted answer, but with relaxed constexpr in C++14, you can just write "normal" code:

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, "!");
Kilkenny answered 28/1, 2017 at 20:48 Comment(0)
L
5

This is how I ended up doing it:

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;
}

Huge thanks to KerrekSB!

Leenaleeper answered 13/11, 2011 at 0:49 Comment(1)
Downvoting, because the answer doesn't really answer the OPs stated questions, but only contains code.Holarctic
T
4

I may be wrong, but I think constexpr functions can also be called with non-constant arguments (in which case they don't give a constant expression and are evaluated at runtime). Which wouldn't work well with non-type template arguments.

Tartuffe answered 13/11, 2011 at 1:22 Comment(2)
Well, presumably, the compiler would complain if you passed a non-compile-time-constant to the function then. Could this be a way to solve the problem where template-ness trickles all the way up layers of code?Hemisphere
@MichaelPrice: Of course it could complain about passing non-const expressions to constexpr functions (and as I said, I'm not sure that I'm right). But that would mean that you'd generally need two functions which are exactly the same except for the constexpr keyword, one for constant expressions and one for all other expressions.Tartuffe
E
3

In order to make use of constexpr with user defined literals, you apparently have to use a variadic template. Take a look at the second listing in the wikipedia article for an example.

Ernestinaernestine answered 12/11, 2011 at 23:58 Comment(4)
You don't always have to use variadic templates to use constexpr, but this case does require it (but I know what you mean).Gun
How can I turn a variadic template of char into int?Leenaleeper
@Leenaleeper - subtract 0x30 from it? I'm not sure of a better way, but you might be able to use partial template specialization for it... though the thought hurts my head.Ernestinaernestine
Why not include an example, instead of referring to Wikipedia?Benediction
W
1

@Pubby. The easy way to digest the char non-type parameter pack is to cature it into an initializer list for a string. Then you can use atoi, atof, etc:

#include <iostream>

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

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

Remember to tack on a null character for the C-style functions.

Wegner answered 13/11, 2011 at 5:48 Comment(1)
The problem with that is it isn't constexpr. It's actually worse than passing unsigned long long as the argument type.Leenaleeper

© 2022 - 2024 — McMap. All rights reserved.