G ++ does not compile constexpr function with assert in it

template<typename T> constexpr inline T getClamped(const T& mValue, const T& mMin, const T& mMax) { assert(mMin < mMax); // remove this line to successfully compile return mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue); } 

error: function body constexpr 'constexpr T getClamped (const T &, const T &, const T &) [with T = long unsigned int]' not return-statement

Using g++ 4.8.1 . clang++ 3.4 does not complain.

Who is there? Anyway, can I do g++ code compilation without using macros?

+6
source share
4 answers

GCC is right. However, there is a relatively simple way:

 #include "assert.h" inline void assert_helper( bool test ) { assert(test); } inline constexpr bool constexpr_assert( bool test ) { return test?true:(assert_helper(test),false); } template<typename T> constexpr inline T getClamped(const T& mValue, const T& mMin, const T& mMax) { return constexpr_assert(mMin < mMax), (mValue < mMin ? mMin : (mValue > mMax ? mMax : mValue)); } 

where we use the comma operator twice.

The first time, because we want to have an assert , which, when true , can be called from the constexpr function. The second, so we can connect two functions into one constexpr function.

As a side benefit, if the constexpr_assert expression cannot be verified as true at compile time, then the getClamped function getClamped not constexpr .

assert_helper exists because the contents of assert is an implementation defined when NDEBUG is true, so we cannot insert it into an expression (it can be an operator, not an expression). This also ensures that the failure of constexpr_assert cannot be constexpr , even if assert is constexpr (say when NDEBUG is false).

The disadvantage of all this is that your statement does not work on the line where the problem occurs, but on 2 calls deeper.

+13
source

With C ++ 14, this is no longer a problem; g++ with the -std=c++14 flag compiles and works fine with your code.

There are three drawbacks:

  • As noted in your question, this one does not work in C ++ 11.
  • assert , of course, will never be run at compile time. Even adding a static_assert with the same condition will not work, since mMin and mMax not considered constant expressions.
  • In addition, since assert does not run at compile time, but the constexpr function, if the condition is false, but the expression evaluates at compile time (for example, constexpr auto foo = getClamped(1,2,0); ), assert will never , which means that incorrect function arguments will not be detected.

In the comment, the user oliora refers to an interesting blog post by Eric Nibler , which describes several approaches that work in C ++ 11 and can be run at compile time or at run time, if necessary.

In short, the strategies are:

  • throw exception; to make it unaccounted for (i.e. more like an assert ), check the constexpr nothrow function
    • Niebler does not call this in his message, but the throw expression must be wrapped with some kind of large logical expression, which is evaluated only if the assert ed condition is false , for example, a ternary expression (which Nibler uses in his example). Standalone if (condition) throw <exception>; the operator will not be allowed even in C ++ 14.
    • Niebler also does not notice that, unlike assert , this approach is independent of NDEBUG ; release creates will trigger failures and crashes.
  • Throw away a custom expression type whose constructor calls std::quick_exit . This eliminates the need for nothrow .
    • Again, this will not be compiled for releases (unless you complete the quick_exit call in ifdef ).
  • Wrap the actual assert inside a lambda, which is passed into a structure that takes an arbitrary callable (as a template parameter) and calls it, then calls std::quick_exit and then throw that structure. This seems like a serious overkill, but, of course, it generates a true statement error message at runtime, which is nice.
    • This is the only approach that will not cause the release build to fail.
    • oliora provides a variant of this approach without throw and quick_exit . It seems a lot cleaner and calmer.
+3
source

constexpr computes at compile time. Transient approval at runtime.

+3
source

g ++ is right. By standard, non-static assert not allowed in the constexpr .

... its body-body should be a compound statement containing only: null,
static_assert declarations,
typedef declarations and declaration aliases that do not define classes or enumerations,
using declarations,
using directives
and exactly one return statement.
- 7.1.5 / 3

+1
source

Source: https://habr.com/ru/post/953281/


All Articles