如何在 constexpr 函数内的字符串文字上静态断言条件



在下面的示例中,main可以static_assert字符串文本是否以 'v' 开头,但verify不能。

为什么会这样?有没有办法允许verify对字符串文字中的字符static_assert条件?

#include <cstddef>
template <std::size_t N>
constexpr char get_first(const char (&str)[N])
{
    static_assert(N>1, "must be > 1");
    return str[0];
}
template <std::size_t N>
constexpr void verify(const char (&str)[N])
{
    static_assert(str[0] == 'v', "must start from v");   
}
int main()
{
    static_assert(get_first("value") == 'v', "first must be 'v'"); // succeeds
    verify("value"); // fails to compile
}

编译错误:

main.cpp: In instantiation of 'constexpr void verify(const char (&)[N]) [with long unsigned int N = 6]':
main.cpp:19:15:   required from here
main.cpp:13:9: error: non-constant condition for static assertion
         static_assert(str[0] == 'v', "must start from v");
         ^~~~~~~~~~~~~
main.cpp:13:9: error: 'str' is not a constant expression

例。

我有另一种解决方法。这不会使用 static_assert但保证在编译时强制执行条件。

#include <type_traits>
template<bool b>
using enforce = std::bool_constant<b>;
template <std::size_t N>
constexpr int verify(const char (&str)[N])
{
    if(get_first(str) != 'v') {
        throw "must start from v";
    }
    return 0;
}
int main()
{
    using assertion = enforce<verify("value")>; // compiles
    using assertion = enforce<verify("fail")>; // fails to compile
    // or use it like
    constexpr auto assertion0 = verify("value"); // compiles
}

这使用抛出在 constexpr 上下文中无效。您将收到的错误将如下所示:

26 : <source>:26:31: error: non-type template argument is not a constant expression
    using assertion = enforce<verify("fail")>; // fails to compile
                              ^~~~~~~~~~~~~~
15 : <source>:15:9: note: subexpression not valid in a constant expression
        throw "must start from v";
        ^
26 : <source>:26:31: note: in call to 'verify("fail")'
    using assertion = enforce<verify("fail")>; // fails to compile

我们可以通过将verify用作模板参数来强制执行 constexpr 评估。这也是声明非 void 返回类型的原因。

在 C++17 中,您可以将值包装在 constexpr lambda 中(在线演示)。呼叫看起来像

verify([=] { return "value"; });

并打开包装,你可以

template <class StringWrapper>
constexpr void verify(StringWrapper str_w)
{
    constexpr auto str = str_w();
    static_assert(str[0] == 'v', "must start from v");   
}

问题是verify()

template <std::size_t N>
constexpr void verify(const char (&str)[N])
{
    static_assert(str[0] == 'v', "must start from v");   
}

可以称为编译时运行时。

所以错误,因为可以在str上执行static_assert(),当调用编译时,但不能在调用运行时(当str的第一个字符不是已知的编译时)。

--编辑--

OP 要求对字符串文本进行编译时检查。

下面是一个愚蠢的例子,我想不是很有用,但我希望能有所启发

template <char const * const str>
constexpr bool startWithUpperLetter ()
 { 
   static_assert( str[0] != 'v', "no start with 'v', please" );
   return (str[0] >= 'A') && (str[0] <= 'Z');
 }
constexpr char const str1[] { "ABC" };
constexpr char const str2[] { "abc" };
constexpr char const str3[] { "vwx" };
int main ()
 {
   static_assert( startWithUpperLetter<str1>() == true, "!" );
   static_assert( startWithUpperLetter<str2>() == false, "!" );
   // static_assert( startWithUpperLetter<str3>() == false, "!" ); // error
 }

最新更新