Robert Ramey
Robert Ramey

Reputation: 1164

constexpr + multidimension array fails

I'm having difficulties understanding constexpr in the context of a C++ builtin multi-dimensional array. The example below illustrates my problem.

a) Looking a value at compile time works as I expect as long as I don't assign it to anything.

b) But if I try to assign it to another constexpr variable, I get a compile time error.

I've looked around and even checked the standards document. Anyone who can explain this to me is truly a guru. FWIW, I'm compiling this with CLang 8.1 under Xcode with C++14 enabled.

using uint8_t = unsigned char;


#if 1
enum class safe_numerics_error : uint8_t {
    success = 0,
    failure,    // result is above representational maximum
    error_count
};
#else
// avoiding enum class fails to solve problem
struct safe_numerics_error {
    const uint8_t m_t;
    constexpr const static uint8_t success = 0;
    constexpr const static uint8_t failure = 1;
    constexpr safe_numerics_error(uint8_t t) :
        m_t(t)
    {}
    constexpr operator uint8_t () const {
        return m_t;
    }
};
#endif

template<typename R>
struct checked_result {
    const safe_numerics_error m_e;
    const union {
        const R m_r;
        char const * const m_msg;
    };
    constexpr /*explicit*/ checked_result(const R & r) :
        m_e(safe_numerics_error::success),
        m_r(r)
    {}
    constexpr /*explicit*/ checked_result(const safe_numerics_error & e) :
        m_e(e),
        m_msg("")
    {}
};

// integers addition
template<class T>
constexpr inline checked_result<T> operator+(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    // "Constexpr variable 'e' must be initialized by a constant expression"
    constexpr const safe_numerics_error x[2][2]{
        // t == success
        {
            // u == ...
            safe_numerics_error::success,
            safe_numerics_error::failure
        },
        // t == positive_overflow_error,
        {
            // u == ...
            safe_numerics_error::failure,
            safe_numerics_error::failure
        }
    };

#if 1  // compile fails
    constexpr const safe_numerics_error e = x
        [static_cast<uint8_t>(t.m_e)]
        [static_cast<uint8_t>(u.m_e)]
    ;

    return
        (safe_numerics_error::success == e)
        ? t.m_r + u.m_r
        : checked_result<T>(e)
    ;
#else  // works as expected
    return
        safe_numerics_error::success == x
            [static_cast<uint8_t>(t.m_e)]
            [static_cast<uint8_t>(u.m_e)]
        ? t.m_r + u.m_r
        : checked_result<T>(x
            [static_cast<uint8_t>(t.m_e)]
            [static_cast<uint8_t>(u.m_e)]
        )
    ;
#endif
}

int main(){
    constexpr const checked_result<unsigned> i = 0;
    constexpr const checked_result<unsigned> j = 0;

    constexpr const checked_result<unsigned> k = i + j;

    // return k.m_r;

    constexpr const checked_result<unsigned> i2 = safe_numerics_error::failure;
    constexpr const checked_result<unsigned> j2 = 0;

    constexpr const checked_result<unsigned> k2 = i2 + j2;
    return k2.m_r;
}

Upvotes: 0

Views: 78

Answers (1)

max66
max66

Reputation: 66200

The problem is that you can't initialize a constexpr variable

constexpr const safe_numerics_error e = x
    [static_cast<uint8_t>(t.m_e)]
    [static_cast<uint8_t>(u.m_e)]

with a value that depend from an argument of a function (or method) (as t and u).

I know that your operator+() is a constexpr one and that in your example you use it only to initialize constexpr values.

But a constexpr function/method can be used compile-time and run-time. So a compiler can't accept code, in a constexpr function/method, that can't be executed run-time.

Upvotes: 4

Related Questions