c++ consteval是否允许在函数参数上使用static_assert?

a8jjtwal  于 2023-06-07  发布在  其他
关注(0)|答案(3)|浏览(214)

目前,您不能使用static_assert来验证constexpr函数的参数,即使对它的所有调用确实都是constexpr。这是有意义的,因为编译器仍然必须创建这个函数的非constexpr示例化,以防其他模块试图调用它。可悲的是,即使函数是static或在匿名名称空间中,情况也是如此。
然而,C++20将引入一个新的关键字consteval,它类似于constexpr,但它不允许以非constexpr的方式调用函数。在这种情况下,编译器可以确定函数参数在编译时总是已知的。因此,理论上应该可以用static_assert验证它们。
问题是:标准允许吗?
示例:

#include <iostream>

consteval char operator""_bchar(const char text[], const size_t length)
{
    static_assert(length == 8, "Binary char has to have 8 digits!"); // <-- This is currently not possible.
    uint8_t byte = 0;
    for (size_t i = 0; i != length; ++i)
    {
        byte <<= 1;
        byte |= text[i] == '1' ? 0b00000001 : 0b00000000;
    }
    return byte;
}

int main()
{
    std::cout << "01000001"_bchar << std::endl;
    return 0;
}

我问这个问题是因为我要写一些用户定义的文字(比示例更复杂)。我可以选择使用编译器扩展来处理验证,或者稍等编译器更新,然后编写完全符合标准的代码。

xmakbtuz

xmakbtuz1#

consteval是否允许在函数参数上使用static_assert?
不。函数参数从来没有,也将继续不能作为常量表达式使用。
在被常量求值的东西和可用作常量表达式的东西之间是有区别的。consteval确保我们处于常量求值上下文中,但它也不会导致所有内容都成为常量表达式。
为了允许函数参数可以用作常量表达式,你需要将所有内容隐式地设置为模板:

template <int> struct X { };

consteval auto foo(int i) {
    static_assert(i > 10); // in order to allow this...
    return X<i>{};         // ... you'd have to allow this too
}

现在foo(20)foo(30)返回不同的类型。那是个模板
要了解为什么这是一个基本的和固有的限制,可以在Andrew萨顿的Translation and evaluation: A mental model for compile-time metaprogramming中找到重要的背景阅读:
有一个编译时求值的心理模型,将其与翻译过程物理分离,这对我非常有帮助。特别是,它帮助我理解了什么是不可能的(例如,在评估期间示例化模板)。这有助于为其他大型和复杂的语言功能修剪设计空间。希望其他人也会发现这篇文章很有帮助。
但是,对于static_assert,您可以添加一个解决方法,只导致编译失败。这只是添加了在常量求值期间不能使用的任何内容。比如:

#define CONSTEVAL_STATIC_ASSERT(c, msg) do { if (!(c)) throw msg; } while(false)

如:

consteval char operator""_bchar(const char text[], const size_t length)
{
    CONSTEVAL_STATIC_ASSERT(length == 8, "Binary char has to have 8 digits!");
    // ...
}
jchrr9hc

jchrr9hc2#

我同意上面的评论者-不可能使用static_assert() w/函数参数,但是,仍然有可能在参数条件下触发consteval函数的编译错误。也就是说,为了获得与static_assert相同的效果。

consteval char operator""_bchar(const char text[], size_t length)
{
    //static_assert(length == 8, "Binary char has to have 8 digits!");
    length /= (length == 8); // Binary char has to have 8 digits!
}

技巧是(length != 8)触发除以零,这不是常量表达式。
编译错误看起来像(gcc-11):

test.cpp: In function ‘int main()’:
test.cpp:110:18:   in ‘constexpr’ expansion of ‘operator""_bchar(((const char*)"12345"), 5)’
test.cpp:104:12: error: ‘(5 / 0)’ is not a constant expression
   104 |     length /= (length == 8); // Binary char has to have 8 digits!
       |     ~~~~~~~^~~~~~~~~~~~~~~~

!!!警告警告!!!:仅适用于consteval函数**。如果在constexpr函数中使用,您的程序将被/除以零错误杀死。使用assert()代替或抛出异常。

pu82cl6c

pu82cl6c3#

我已经修改了@巴里的答案中的技术,以便在所有constevalconstexpr和常规函数中工作。

#include <assert.h>
#include <type_traits>

#define constexpr_assert(expression) do{if(std::is_constant_evaluated()){if(!(expression))throw 1;}else{assert(!!(expression));}}while(0)

当对C++23的支持更好时,可以使用if consteval来改进它:

#include <assert.h>

#define constexpr_assert(expression) do{if consteval{if(!(expression))throw 1;}else{assert(!!(expression));}}while(0)

相关问题