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

Tim*_*mmm 5 constexpr c++11

我正在尝试创建一个constexpr将UUID字符串转换为类似"f6ece560-cc3b-459a-87f1-22331582216e"这样的类的函数:

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

这是我到目前为止所得到的:

// 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")
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样存在问题 - 似乎不可能在constexpr函数中使用常量表达式的函数参数,因此您无法static_assert对参数执行操作,即使传入的值是常量表达式也是如此.

所以我使用宏来检查字符串长度,并放弃检查字符.

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

编辑:这与constixpr函数中的C++ 11 - static_assert不同? - 或者至少相同的答案不起作用 - 请参阅下面的评论.

编辑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>();
}
Run Code Online (Sandbox Code Playgroud)

这不起作用:

// 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]>(),
Run Code Online (Sandbox Code Playgroud)

因为str[0]不是一个恒定的表达.

Sha*_*our 5

正如 AndyG指出的问题C++11 - constexpr 函数中的 static_assert?告诉我们这样做的一种方法是使用非类型模板参数,这些参数必须在编译时可用。

此解决方案的问题在于 OP 正在使用无法绑定到非类型参数的字符串文字:

特别是,这意味着字符串文字、数组元素的地址和非静态成员的地址不能用作模板参数来实例化其对应的非类型模板参数是指向对象的指针。

一种解决方法不是直接使用字符串文字,而是使用对问题很重要的属性,即数组的长度,如下所示:

template <size_t N>
constexpr std::array<uint8_t, 16> UUIDFromString( const char (&str)[N])
{
  static_assert(N == 36, "Invalid GUID length");

  //....
}
Run Code Online (Sandbox Code Playgroud)