4

昨天我读了几个答案static_assert(false, "Some message"),关于 an 的内部else子句的使用if constexpr。我知道根据标准,它被认为是格式错误的(即使某些编译器,包括 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可以专门化为一个为表达式返回truebool_value<false, TypeOfValue>::value的版本。

4

3 回答 3

3

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

另一个答案提到,但我不同意它的[temp.res]/8解释方式。

可以在任何实例化之前检查模板的有效性。...程序格式错误,不需要诊断,如果:

— 不能为模板或 constexpr 的子语句生成有效的特化,如果模板中的语句没有实例化,或者...

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

根据我的理解(并且根据常识,我希望),这部分标准的重点是允许编译器if constexpr及早检查模板和分支的有效性(当它们第一次看到时),并拒绝那些那不可能被实例化。

模板中的每个分支可能被实例化,因为以后bool_value() 可以专门化。我怀疑一个理​​智的编译器会因为尚未bool_value()专门化而拒绝您的代码。

于 2020-06-20T12:24:00.180 回答
1

您链接的问题的公认答案解释了为什么它格式错误。请注意 Standard temp.res-8中的引用,它说(强调我的)

可以在任何实例化之前检查模板的有效性。[...] 程序格式错误,不需要诊断,如果:

  • 无法为模板或 constexpr 的子语句生成有效的特化,如果模板内的语句和模板未实例化,或者...

[...]

否则,不应为可以生成有效专业化的模板发出诊断。[注:如果模板被实例化,错误将根据本文档中的其他规则进行诊断。准确诊断这些错误的时间是实施质量问题。——尾注 ]

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

这种包罗万象的语句的常见解决方法是依赖于类型的表达式,该表达式始终为假:

模板<class T> structdependent_false : std::false_type {};
模板 <类型名 T>
无效 f() {
     if constexpr (std::is_arithmetic_v<T>)
         // ...
     别的
       static_assert(dependent_false<T>::value, "必须是算术"); // 好的
}

正如Evg在评论中指出的那样

从理论上讲,这仍然是不正确的,因为 [...] 始终是false,除非您实际上有一个返回 true 的专业化。

因此,您可以将该专业化添加到上一个片段中

命名空间{
    // 不要释放。
    结构积极的独角兽类型;
}

模板<> structdependent_false<aggressive_unicorn_type> : std::true_type {};

这是否有必要满足标准文档的字母,这取决于可以生成”的解释。

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

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

#include <type_traits>

无效 f(int) {};
无效 g(无符号) {};

模板<类 T>
constexpr inline bool is_supported =
    std::is_same_v<T, int> ||
    std::is_same_v<T, 无符号> ||
    std::is_same_v<T, char>;

模板 <class T>
无效使用(T值){
    static_assert(is_supported<T>, "不支持");
    if constexpr (std::is_same<T, int>::value) {
        f(值);
    } else if constexpr (std::is_same_v<T, unsigned>){
        g(值);
    } 别的 {
        static_assert(!is_supported<T>, "尚未实现");
    }
}
于 2020-06-19T11:47:48.773 回答
1

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

在阅读了其他几个答案后,我的结论是:

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

  2. 一个永远无法编译的程序是格式错误的。

所以,问题是允许编译器提前评估 static_assert,即使在if constexpr.

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

于 2020-06-20T11:52:49.940 回答