17

目前,您不能使用static_assert来验证函数的参数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;
}

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

4

1 回答 1

13

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 Sutton 的翻译和评估:编译时元编程的心理模型中找到重要的背景资料,以了解为什么这是一个基本和固有的限制:

拥有一个编译时评估的心理模型,将其与翻译过程物理分离,这对我非常有帮助。特别是,它帮助我理解了什么是不可能的(例如,在评估期间实例化模板)。这有助于修剪设计空间以适应大型和复杂的语言功能。希望其他人也会发现此说明也很有帮助。


但特别是,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!");
    // ...
}
于 2019-07-26T20:47:26.340 回答