c++ - C++11 constexpr 函数中的常量表达式字符串参数

标签 c++ c++11 constexpr

我正在尝试制作 constexpr转换 UUID 字符串的函数,如 "f6ece560-cc3b-459a-87f1-22331582216e"到这样的类(class):

class UUID {
public:
      explicit UUID(uint8_t bytes[]); // Must be 16 byte array.

这是我到目前为止所得到的:
// Compile time hex conversion of a single character into a nibble (half-byte).
constexpr uint8_t hexToNibble(char a)
{
    // Does not work:
//  static_assert(a >= '0' && a <= '9' || a >= 'a' && a <= 'f' || a >= 'A' && a <= 'F', "Invalid hex character");
    return a >= '0' && a <= '9' ? (a - '0') :
           a >= 'a' && a <= 'f' ? (a - 'a' + 10) :
           a >= 'A' && a <= 'F' ? (a - 'A' + 10) : 0;
}

// Compile time hex conversion of two characters into a byte.
constexpr uint8_t hexToByte(char a, char b)
{
    return (hexToNibble(a) << 4) + hexToNibble(b);
}

// Compile time string length.
constexpr int strlenConst(const char* str)
{
    return *str ? 1 + strlenConst(str + 1) : 0;
}

// Convert a UUID string to an array of bytes.
// Example: "f6ece560-cc3b-459a-87f1-22331582216e".
constexpr std::array<uint8_t, 16> UUIDFromString(const char* str)
{
    // This does not work:
//  static_assert(strlenConst(str) == 36, "Invalid GUID length");

    return std::array<uint8_t, 16>{
        hexToByte(str[0], str[1]),
        hexToByte(str[2], str[3]),
        hexToByte(str[4], str[5]),
        hexToByte(str[6], str[7]),
        hexToByte(str[9], str[10]),
        hexToByte(str[11], str[12]),
        hexToByte(str[14], str[15]),
        hexToByte(str[16], str[17]),
        hexToByte(str[19], str[20]),
        hexToByte(str[21], str[22]),
        hexToByte(str[24], str[25]),
        hexToByte(str[26], str[27]),
        hexToByte(str[28], str[29]),
        hexToByte(str[30], str[31]),
        hexToByte(str[32], str[33]),
        hexToByte(str[34], str[35]),
    };
}

#define MAKE_UUID(var, str) \
    static_assert(strlenConst(str) == 36, "Invalid GUID length for " #var); \
    const UUID var(UUIDFromString(str).data());

// Works but doesn't check string length.
const UUID UUID_1(UUIDFromString("f6ece560-cc3b-459a-87f1-22331582216e").data());

// Checks string length but uses an ugly macro.
MAKE_UUID(UUID_2, "f6ece560-cc3b-459a-87f1-22331582216e")

正如你所看到的,有一个问题——在 constexpr 函数中似乎不可能有常量表达式的函数参数,所以你不能做 static_assert s 在参数上,即使传入的值是一个常量表达式。

所以我求助于一个宏来检查字符串长度,并放弃检查字符。

有没有解决的办法?另外,如何轻松确保在编译时实际评估此函数?

编辑 : 这和C++11 - static_assert within constexpr function?不一样- 或者至少相同的答案不起作用 - 请参阅下面的评论。

编辑 2 :Shafik 的优秀答案适用于大小问题,但不适用于检查十六进制字符。据我所知这是不可能的 - 即使你使用这个......
// Compile time hex conversion of a single character into a nibble (half-byte).
template<char a>
constexpr uint8_t hexToNibble()
{
    static_assert(a >= '0' && a <= '9' || a >= 'a' && a <= 'f' || a >= 'A' && a <= 'F', "Invalid hex character");
    return a >= '0' && a <= '9' ? (a - '0') :
           a >= 'a' && a <= 'f' ? (a - 'a' + 10) :
           a >= 'A' && a <= 'F' ? (a - 'A' + 10) : 0;
}

// Compile time hex conversion of two characters into a byte.
template<char a, char b>
constexpr uint8_t hexToByte()
{
    return (hexToNibble<a>() << 4) + hexToNibble<b>();
}

这行不通:
// Convert a UUID string to an array of bytes.
// Example: "f6ece560-cc3b-459a-87f1-22331582216e".
template <size_t N>
constexpr std::array<uint8_t, 16> UUIDFromString(const char (&str)[N])
{
    // Note you have to include the null byte.
    static_assert(N == 37, "Invalid GUID length.");

    return std::array<uint8_t, 16>{
        hexToByte<str[0], str[1]>(),
        hexToByte<str[2], str[3]>(),

因为 str[0]不是一个常量表达式。

最佳答案

作为安迪points out问题C++11 - static_assert within constexpr function?告诉我们这样做的一种方法是使用非类型模板参数,这些参数必须在编译时可用。

此解决方案的问题在于 OP 使用的字符串文字是 can not be bound to non-type arguments :

In particular, this implies that string literals, addresses of array elements, and addresses of non-static members cannot be used as template arguments to instantiate templates whose corresponding non-type template parameters are pointers to objects.



一种解决方法不是直接使用字符串文字,而是使用对问题很重要的属性,即数组的长度,如下所示:
template <size_t N>
constexpr std::array<uint8_t, 16> UUIDFromString( const char (&str)[N])
{
  static_assert(N == 36, "Invalid GUID length");

  //....
}

关于c++ - C++11 constexpr 函数中的常量表达式字符串参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29166329/

相关文章:

c++ towupper() 不转换某些字符

c++ - 为什么不将比较因素纳入 Big O 计算?

c++ - std::regex_search() 不接受我的论点

c++ - gcc v10 和 v9 : bug or feature 之间的 constexpr 差异

c++ - Constexpr 构造和静态成员不起作用

c++ - 为什么或为什么不在 C++ 中使用 memset?

c++ - std::shared_ptr 的原始指针返回基而不是派生

c++ - 图节点父列表中的 weak_ptr VS shared_ptr

c++ - 为什么传递临时对象时不调用复制构造函数

c++ - Constexpr 成员函数