Search code examples
c++language-lawyerconstexprstatic-assertif-constexpr

Is this use of static_assert inside if constexpr well-formed?


I read a couple of answers yesterday, on the use of static_assert(false, "Some message") inside the else clause of an if constexpr. I understand that it's considered to be ill-formed, according to the standard (even if some compilers, including MSVC2017, will accept it). Qt will also mark this as an error.

My question is, is the code below well-formed according to the standard? (I'm inclined to think so, but I'd like a confirmation.)

template <typename TypeOfValue>
static void PushValue(duk_context* ctx, TypeOfValue value) {
    // Push value onto duktape stack
    if constexpr (std::is_same<TypeOfValue, int>::value) {
        // Push int
        duk_push_int(ctx, value);
    } else if constexpr (std::is_same<TypeOfValue, uint32_t>::value) {
        // Push uint
        duk_push_uint(ctx, value);
    } else {
        // Unsupported type
        static_assert(bool_value<false, TypeOfValue>(), "Unsupported type");
    }    
}

template <bool value, typename T>
static constexpr bool bool_value() {return value;}        

Edit:

It seems, from the comment I got, that bool_value should instead be defined like this:

template<bool value, typename T>
struct bool_value { 
    static constexpr bool value = value; 
};

with the usage pattern

// Unsupported type
static_assert(bool_value<false, TypeOfValue>::value, "Unsupported type");

It's then well-formed, only because it's possible for bool_value to be specialized into a version which returns true for the expression bool_value<false, TypeOfValue>::value.


Solution

  • Both of your attempts (with the function and with the struct) are well-formed as is.

    The other answer mentions [temp.res]/8, but I disagree with how it was interpreted.

    The validity of a template may be checked prior to any instantiation. ... The program is ill-formed, no diagnostic required, if:

    — no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or ...

    Both the function and struct you wrote can be specialized to be true. I believe the mere possibility of specialization is enough, you don't actually need to add a dummy true specialization to make the code well-formed.

    According to my understanding (and according to common sense, I hope), the point of this part of the standard is to allow compilers to check validity of templates and if constexpr branches early (when they are seen for the first time), and reject the ones that can't possibly be instantiated.

    Every branch in your template can potentially be instantiated, because bool_value() can be specialized later. I doubt a sane compiler is going to reject your code due to bool_value() not being specialized yet.