2

I made some templates which are supposed to

  • (1) test if numbers can be added without overflow,
  • (2) add numbers and fail in case of overflow.

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;
}
6
  • 1
    "The (1) test_sum inside static_assert compiles" That's not present in the code shown. It never calls test_sum, under static_assert or otherwise. Commented Apr 29 at 23:14
  • 1
    Missing a few headers. type_traits, limits, and stdexcept, I think. Commented Apr 29 at 23:15
  • 2
    Once headers are added, code compiles. If you're asking about code that doesn't work, post the code that doesn't work. If you post code that works, many of the readers will move on without looking much deeper to find the actual problem. Some of those will vote to close as non-reproducible. Commented Apr 29 at 23:19
  • @IgorTandetnik yees, my bad. Added it. Commented Apr 30 at 9:13
  • 1
    What I mean is don't comment out the failure. A reader of this question should be able to take your code, paste it into their tools, compile it, and see the same errors--with some expected variance to the diagnostics depending on the compiler used--that you do. In this case you have three attempts, so leave one in just so that the reader gets an error and is less likely to write your question off as a bad one. Commented Apr 30 at 23:04

1 Answer 1

7
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.

Sign up to request clarification or add additional context in comments.

4 Comments

Ah, so I guess I would have to make all "args" as template params, only then it would truly be a constant expression?
Also, I had no idea I can throw in constexpr functions... will have to read deeper what is allowed in there. It seems to work now. It only is unfortunate that MSVC gives a very nondescript expression did not evaluate to a constant without mentioning what actually failed... Thanks @Jarod42 for the comparison.
@herhor67 yes, template parameters are constant expressions in the function. Prior to C++26, throwing is not permitted in constant expressions, but you are allowed to have a throw expression in a constexpr function. When that expression is evaluated, that simply makes the expression not a constant expression, and you get an error message, which is not too different from actually throwing, except that you cannot catch.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.