How to dispatch between assert() and static_assert(), dependend if in constexpr context?

Better than a comma expression, you can use a ternary conditional. The first operand is your assertion predicate, the second operand is your success expression, and since the third operand can be any expression - even one not usable in a C++11 constant context - you can use a lambda to invoke your library's ASSERT facility:

#define ASSERT_EXPR(pred, success)    \
    ((pred) ?                         \
     (success) :                      \
     [&]() -> decltype((success))     \
     {                                \
         ASSERT(false && (pred));     \
         struct nxg { nxg() {} } nxg; \
         return (success);            \
     }())

Explanation of the body of the lambda:

  • ASSERT(false && (pred)) is to ensure that your assertion machinery is invoked with an appropriate expression (for stringification).
  • struct nxg { nxg() {} } nxg is for future-safety, to ensure that if you compile in C++17 or above with NDEBUG the lambda is still non-constexpr and so the assertion is enforced in const-evaluation context.
  • return (success) is there for two reasons: to ensure that the second and third operands have the same type, and so that if your library respects NDEBUG the success expression is returned regardless of pred. (pred will be evaluated, but you'd hope that assertion predicates should be cheap to evaluate and have no side effects.)

Example of use:

template<int Size>
struct Array {
  int m_vals[Size];
  constexpr int getElement( int idx ) const
  {
    return ASSERT_EXPR(idx < Size, m_vals[idx]);
  }
};

constexpr int I = Array<2>{1, 2}.getElement(1); // OK
constexpr int J = Array<2>{1, 2}.getElement(3); // fails

Something like

void assert_impl() { assert(false); } // Replace body with own implementation

#ifdef NDEBUG // Replace with own conditional
#define my_assert(condition) ((void)0)
#else
#define my_assert(condition) ((condition) ? (void()) : (assert_impl(), void()))
#endif

template<int Size>
struct Array {
  int m_vals[Size];
  constexpr const int& getElement( int idx ) const
  {
    return my_assert(idx < Size), m_vals[idx];
  }
};

It will give a compile time error on assertion failure if used in a context requiring a constant expression (because it will call a non-constexpr function).

Otherwise it will fail at runtime with a call to assert (or your analogue).

This is the best that you can do as far as I know. There is no way to use the value of idx to force a check at compile-time outside of context requiring constant expressions.

The comma operator syntax is not nice, but C++11 constexpr functions are very limited.

Of course, as you already noted, undefined behavior will be diagnosed anyway if the function is used in a context requiring a constant expression.

If you know that assert (or your analogue) doesn't expand to anything that is forbidden in a constant expression if the condition evaluates to true but does so if it evaluates to false, then you can use it directly instead of my_assert and skip the indirection that I build in my code.


static_assert cannot be used here. The argument to a constexpr function is not permitted in a constant-expression. Therefore, there is no solution to your problem under the given constraints.

We can, however, solve the problem by bending two constraints

  1. not using static_assert (use other methods to produce a compile-time diagnostic instead), and

  2. disregard that the comma operator "is ugly and some tools spit warnings about it." (Showing its ugliness is an unfortunate consequence of the strict requirements of C++11 constexpr functions)

Then, we can use a normal assert:

template <int Size>
struct Array {
  int m_vals[Size];
  constexpr const int& getElement(int idx) const
  {
    return assert(idx < Size), m_vals[idx];
  }
};

In a constant evaluation context, this will emit a compiler error like error: call to non-'constexpr' function 'void __assert_fail(const char*, const char*, unsigned int, const char*)'.