如何确保在constexpr函数中接受一个数组,该数组以null结束

How to make sure in a constexpr function taking an array that the array is NULL-terminated?

本文关键字:数组 结束 一个 null 确保 何确保 constexpr 函数      更新时间:2023-10-16

下面的代码用来创建一个长度不超过8个字符的字符串的散列:

#include <type_traits>
#include <cstdint>
#include <iostream>
template<std::size_t N, std::size_t n=N>
constexpr typename std::enable_if<N<=9 && n==0,
uint64_t>::type string_hash(const char (&)[N])
{
    return 0;
}
template<std::size_t N, std::size_t n=N>
constexpr typename std::enable_if<N<=9 && n!=0,
uint64_t>::type string_hash(const char (&array)[N])
{
    return string_hash<N,n-1>(array) | ((array[n-1]&0xffull)<<(8*(n-1)));
}

对于普通字符串字面值和constexpr以null结尾的字符串,它确实正常工作。但是如果我这样做:

constexpr char s2[] = {1,2,3,4,5,6,7,8,9};
std::cout << string_hash(s2) << "n";

,输出将与字符串"x1x2x3x4x5x6x7x8"相同。我试过在string_hash的定义中添加static_assert(array[N-1]==0,"Failed");,但编译器说array[N-1]不是常量表达式。然后我尝试声明参数constexpr,但是编译器说不能声明参数constexpr

我该如何检查呢?

请记住,虽然constexpr函数可以在编译时使用,但它们并不需要。您不能在运行时参数上添加任何静态断言,因为当编译时不知道参数时,静态断言将无法求值。

你能做的和你对非constexpr函数做的是一样的:抛出一些东西。这并不能防止用无效输入调用函数,但可以防止无声的错误结果。当你的函数在需要常量表达式的上下文中使用时,编译器会正确地检测到它没有返回常量值。

在c++ 11中,constexpr函数体需要是一个单独的返回语句,但您仍然可以在那里容纳它:

return array[N-1] ? throw "bad!" : <your current return expression here>;

一定要挑个好点的来扔。

编译器抱怨的原因是因为string_hash<N,n>函数实例化时array是未知的。static_cast在实例化时求值,而不是在调用时求值(即使它是constexpr函数)。

请注意,将为每一对<N,n>值创建一个函数。如果你使用两个相同长度的constexpr字符串,将使用完全相同的string_hash实例,但根据参数array[N-1]可能会给出不同的结果。

如果需要的话,我会继续为你的问题寻找一个准确的答案。然而,作为一个"快速修复",我可以建议改变哈希函数,使它始终包括计算中的最后一个字符,是0还是不是?

更新:在做了一些挖掘之后,我了解到某种constexpr_assert可能是您在案例中需要的,并且目前在标准中缺少它。希望他们将来会添加它。您可能需要检查:

  • g++没有't编译包含assert的constexpr函数
  • constexpr函数的可选断言
  • http://ericniebler.com/2014/09/27/assert-and-constexpr-in-cxx11