61

How would one properly do a static_assert within a constexpr function? For example:

constexpr int do_something(int x)
{
  static_assert(x > 0, "x must be > 0");
  return x + 5;
}

This is not valid C++11 code, because a constexpr function must only contain a return statement. I don't think that the standard has an exception to this, although, the GCC 4.7 does not let me compile this code.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
RétroX
  • 1,996
  • 4
  • 16
  • 24

2 Answers2

70

This is not valid C++11 code, because a constexpr function must only contain a return statement.

This is incorrect. static_assert in a constexpr function are fine. What is not fine is using function parameters in constant expressions, like you do it.

You could throw if x <= 0. Calling the function in a context that requires a constant expression will then fail to compile

constexpr int do_something(int x) {
  return x > 0 ? (x + 5) : (throw std::logic_error("x must be > 0"));
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 12
    Cool, I didn't know `throw`s in a `constexpr` function that is called in a constexpr context will cause the compilation to fail! – Xeo Dec 24 '11 at 19:36
  • 18
    @Xeo doing *anything* non-constexpressy on the other side of ?: will do the job. :) – Johannes Schaub - litb Dec 24 '11 at 19:44
  • Nice complement to `static_assert` I must say. :) – Xeo Dec 24 '11 at 19:46
  • 6
    I'm confused why "using function parameters in constant expressions" is "not fine". If 'x' is a constant expression then (x+5) is also a constant expression and can be evaluated at compile-time. If 'x' isn't a constant expression then the function itself loses its constexpr-ness (for that particular invocation) and will simply be evaluated at run-time. Could someone clarify? – monkey0506 Jan 18 '13 at 07:53
  • @JohannesSchaub-litb does an alternative exist which does not use throw? In particular for library code, one must think that their clients might want to compile their programs with exceptions turned off. In this case this fails with gcc-4.8 even if the throw case never happens. – gnzlbg Apr 07 '13 at 17:13
  • 6
    @monkey_05_06: Because 'x' is a function argument and therefore **not** a constexpr. Remember, a constexpr function must also be suitable as a runtime function (with non-constexpr args), and thus its args are ineligible for use in a static_assert(...) declaration. This is regardless of whether you ever actually call the function from a non-constexpr context, which is why the requirement that a constexpr function must also be runtime-callable is as much of a restriction as it is a "feature". – etherice Apr 26 '13 at 02:15
  • 2
    @gnzlbg: See his comment above about using anything that isn't a `constexpr`. What I did (as a generic solution) was declare a **non-**constexpr function template like so... `template RT non_constexpr() { return RT{}; }` ...to use like this... `return (x > 0) ? (x + 5) : non_constexpr();` ... tested in g++ and clang++. – etherice Apr 26 '13 at 02:43
  • @JohannesSchaub-litb Can you give an example? I can't use exceptions (writing for a microcontroller so they are disabled). – Timmmm Mar 20 '15 at 13:35
  • 1
    Actually this is not that great as it only works when the result is a constexpr. E.g. `const int foo = do_something(-1);` still compiles (which is weird because I would have thought it ran the constexpr code in that case?) – Timmmm Mar 20 '15 at 13:43
  • @Timmmm No, the function is only evaluated as `constexpr` if it is called in a `constexpr` context, which of course, initialising/assigning to a non-`constexpr` object is no such context. When declaring an object, `constexpr` implies `const` but **not** the other way around. But _cppist_'s answer might work for you, i.e. if you want the arguments always to be `constexpr`, then make them _template_ arguments, not function arguments. – underscore_d Aug 13 '16 at 19:21
  • @JohannesSchaub-litb That is very cool! But I don't get it why throw in a constexpr function that is called in a constexpr context will cause the compilation to fail. Is it the behavior defined in c++ standard? – camino Oct 24 '18 at 01:23
  • @JohannesSchaub-litb I test it with if (x<=0) throw "error"; it will report error in compile time either. Is that means when x>0, throw "error" will not be compiled ? – camino Oct 24 '18 at 01:31
24

This works and is valid C++11 code, because template arguments are compile time only:

template <int x>
constexpr int do_something() {
    static_assert(x > 0, "x must be > 0");
    return x + 5;
}

I faced with the same problems as you did with constant expressions in C++. There's few clear documentation about constexprs at the moment. And note that there's some known bugs with it in gcc's issue tracker, but your problem seems not to be a bug.

Note that if you declare constexpr functions inside classes, you are not able to use them inside the class. This also seems to be not a bug.

Edit: This is allowed according to the standard: 7.1.3 states

... or a compound-statement that contains only

  • null statements,
  • static_assert-declarations
  • typedef declarations and alias-declarations that do not
    define classes or enumerations,
  • using-declarations,
  • using-directives,
  • and exactly one return statement
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
cppist
  • 639
  • 5
  • 8