c++templatesconstexprstatic-assert

Template to check overflow and static_assert


I made some templates which are supposed to

The (1) test_sum inside static_assert compiles, so I guess it is constexpr.
I wanted to avoid duplicating the math, so I tried to put the assert directly inside the (2) function that calculates the sum. However, the assert always fails, no matter the condition, in all the versions below. Can this be solved in any way?

#include <limits>
#include <stdexcept>
#include <type_traits>

template<typename T>
constexpr bool add(T& a, T b)
{
    static_assert(std::is_integral<T>::value, "Only integral types are supported");

    if ((b > 0) && (a > std::numeric_limits<T>::max() - b))
        return false;
    if ((b < 0) && (a < std::numeric_limits<T>::min() - b))
        return false;

    a += b;
    return true;
}

//

template<typename T>
constexpr bool sum_impl(T& result)
{
    return true;
}

template<typename T, typename... Rest>
constexpr bool sum_impl(T& result, T value, Rest... rest)
{
    if (!add(result, value))
        return false;

    return sum_impl(result, rest...);
}

//

template<typename T, typename... Rest>
constexpr bool test_sum(T value, Rest... rest) // (1)
{
    return sum_impl(value, rest...);
}

template<typename T, typename... Rest>
T sum(T value, Rest... rest) // (2) regular
{
    if (!sum_impl(value, rest...))
        throw std::overflow_error("Overflow in checked::sum");

    return value;
}

template<typename T, typename... Rest>
constexpr T sum_cexpr(T value, Rest... rest) // (2) constexpr
{
//  if (!sum_impl(value, rest...))
//      static_assert(false, "Overflow"); // fail

//  static_assert(sum_impl(value, rest...), "Overflow"); // fail

//  if (!sum_impl(value, rest...))
//      delegated_assert<false>(); //  fail

    return value;
}

template<bool B>
constexpr void delegated_assert()
{
    if constexpr (!B)
        static_assert(B);
}

//////

int main()
{
    static_assert(test_sum(1, 2)); // (1) works

    constexpr int a = sum_cexpr(10, 20, 30); // fails to compile
    
    return 0;
}

Solution

  • static_assert(sum_impl(value, rest...), "Overflow");
    

    This cannot possibly work because value and rest aren't constant expressions. Just because this static_assert is inside a constexpr function doesn't mean that you could read out their values within static_assert.

    See also Can't use function parameter of a constexpr function in a constant expression

    static_assert(false, "Overflow"); and delegated_assert<false>(); are obviously going to fail as well. static_assertions are evaluated at compile time, and it is irrelevant whether you put them into an if statement. You can only have such "conditional static_asserts in an if constexpr statement, but that wouldn't work for the same reason that the aforementioned static_assert always fails.

    In general, the static_asserts seem unnecessary here. If an overflow is detected, an exception will be thrown, and

    constexpr int a = sum(10, 20, 30);
    

    ... won't compile.