如果 constexpr 格式正确,是否在内部使用了 static_assert?

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

我昨天读了一些 ,关于在 if constexprelse 子句中使用 static_assert(false, "Some message")。我知道根据标准,它被认为是格式错误(即使某些编译器(包括 MSVC2017)会接受它)。 Qt 也会将此标记为错误。

我的问题是,下面的代码格式正确是否符合标准? (我倾向于这么认为,但我想确认一下。)

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;}        

编辑:

从我收到的评论来看,bool_value 似乎应该这样定义:

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

使用模式

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

它是合式的,只是因为 bool_value 可以专门化为 returns true 用于表达式 bool_value<false, TypeOfValue>::value

of the question you linked explains why it's ill-formed. Note the quote from the Standard temp.res-8,上面写着(强调我的)

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

[...]

Otherwise, no diagnostic shall be issued for a template for which a valid specialization can be generated. [ Note: If a template is instantiated, errors will be diagnosed according to the other rules in this document. Exactly when these errors are diagnosed is a quality of implementation issue. — end note ]

在关于 if 的 cppreference page 中,您可以看到以下解决方法(强调我的)

The common workaround for such a catch-all statement is a type-dependent expression that is always false:

template<class T> struct dependent_false : std::false_type {};
template <typename T>
void f() {
     if constexpr (std::is_arithmetic_v<T>)
         // ...
     else
       static_assert(dependent_false<T>::value, "Must be arithmetic"); // ok
}

comments by Evg

中所述

theoretically, this is still ill-formed, because [...] is always false unless you actually have a specialization that returns true.

因此您可以将该专业化添加到之前的代码片段中

namespace {
    // Do not unleash.
    struct aggressive_unicorn_type;
}

template<> struct dependent_false<aggressive_unicorn_type> : std::true_type {};

这是否有必要完全满足标准文档,这取决于对 的解释能否 生成 .

我想知道这是否真的很重要。 OP 询问代码是否格式正确,但他们实际上是在尝试生成格式错误的代码,并在某些条件下进行诊断(这就是 static_assert(false) 所暗示的,参见 dcl.pre) .

正如提问者已经指出的那样,当传递“错误”类型的参数时,还有其他方法可以强制编译器出错(考虑即将到来的标准的概念),也许,如果我们想使用 static_asserts 与 if constexpr 结合使用时,最好的选择可能是依赖于类型的表达式,即 not 始终为 false。

#include <type_traits>

void f(int) {};
void g(unsigned) {};

template< class T>
constexpr inline bool is_supported =
    std::is_same_v<T, int> ||
    std::is_same_v<T, unsigned> ||
    std::is_same_v<T, char>;

template <class T>
void use(T value) {
    static_assert(is_supported<T>, "Not supported");
    if constexpr (std::is_same<T, int>::value) {
        f(value);
    } else if constexpr (std::is_same_v<T, unsigned>){
        g(value);
    } else {
        static_assert( !is_supported<T>, "Not yet implemented");
    }
}

我将添加我自己的最终想法作为答案,因为我似乎没有从其他来源获得足够原则性的答案

我在阅读了几个 other 答案后得出的结论是:

  1. 根据标准,允许编译器提前评估static_assert(false, "...")。如果是,它会生成一个永远无法编译的程序。

  2. 无法编译的程序格式错误。

因此,问题是允许编译器尽早评估 static_assert,即使在 if constexpr.

中也是如此

通过引入人为依赖模板参数的 false 强制它延迟评估直到实例化时间,对我来说似乎有点做作。但我承认还有其他考虑因素可能使其成为正确的语言选择。 (这就是我希望专家回答能够阐明的那种事情。)

您的两次尝试(使用函数和结构)都是格式正确的。

mentions [temp.res]/8,但我不同意它的解释方式。

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

您编写的函数和结构 可以 专门化为 true。我相信仅仅有专业化的可能性就足够了,您实际上不需要添加虚拟 true 专业化来使代码格式正确。

根据我的理解(我希望是根据常识),标准这部分的要点是允许编译器尽早检查模板和 if constexpr 分支的有效性(当它们出现时第一次),拒绝无法实例化的。

模板中的每个分支 都可以 可能被实例化,因为 bool_value() 可以 稍后被专门化。我怀疑一个理​​智的编译器会拒绝你的代码,因为 bool_value() 没有被专门化 yet.