Vittorio Romeo Vittorio Romeo - 1 month ago 11
C++ Question

g++ doesn't 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: body of constexpr function 'constexpr T getClamped(const T&, const T&, const T&) [with T = long unsigned int]' not a return-statement


Using
g++ 4.8.1
.
clang++ 3.4
doesn't complain.

Who is right here? Any way I can make
g++
compile the code without using macros?

Answer

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

#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 abuse the comma operator, twice.

The first time because we want to have an assert that, when true, can be called from a constexpr function. The second, so we can chain two functions into a single constexpr function.

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

The assert_helper exists because the contents of assert are implementation defined when NDEBUG is true, so we cannot embed it into an expression (it could be a statement, not an expression). It also guarantees that a failed constexpr_assert fails to be constexpr even if assert is constexpr (say, when NDEBUG is false).

A downside to all of this is that your assert fires not at the line where the problem occurs, but 2 calls deeper.

Comments