C++11 - static_assert within constexpr function?
Asked Answered
C

2

62

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.

Cassycast answered 24/12, 2011 at 18:0 Comment(0)
I
71

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"));
}
Incursive answered 24/12, 2011 at 19:22 Comment(12)
Cool, I didn't know throws in a constexpr function that is called in a constexpr context will cause the compilation to fail!Quathlamba
@Quathlamba doing anything non-constexpressy on the other side of ?: will do the job. :)Incursive
Nice complement to static_assert I must say. :)Quathlamba
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?Kanchenjunga
@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.Extraction
@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".Hogue
@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<typename RT> RT non_constexpr() { return RT{}; } ...to use like this... return (x > 0) ? (x + 5) : non_constexpr<int>(); ... tested in g++ and clang++.Hogue
@JohannesSchaub-litb Can you give an example? I can't use exceptions (writing for a microcontroller so they are disabled).Familiar
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?)Familiar
@Familiar 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.Crumple
@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?Mamoun
@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 ?Mamoun
A
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
Antarctica answered 24/8, 2012 at 12:44 Comment(5)
No. constexpr must be only a single return statement.Straggle
I read the standard. You are correct, this is fine. I edited your answer to add that.Straggle
Thank you. This way works for me very well. I can not use exceptions, because they are disabled in my build (small embedded system).Dunlin
How do you call this function?Agnesse
@marsh: do_something<x>(); -which means x must be a compile-time value.Styles

© 2022 - 2024 — McMap. All rights reserved.