我有一个用户定义的文字运算符,仅对特定长度的字符串有意义,如下所示:
constexpr uint16_t operator "" _int(const char* s, std::size_t len)
{
return len == 2 ? s[0] | (s[1] << 8) : throw;
}
这有效:
"AB"_int // equals 16961
但这也可以编译,但我不希望它这样:
"ABC"_int // throws at runtime
我尝试了
static_assert(len == 2)
,但在 constexpr 函数中不允许这样做。
如何让
"ABC"_int
在编译时导致错误?
如何让
在编译时导致错误?"ABC"_int
举例:初始化一个
constexpr
变量
constexpr auto foo = "ABC"_int;
否则(如果您不以某种方式强制计算编译时间)编译器不会计算(不是强制性的,但实际上会发生)编译时间,而是为运行时编译准备代码。
在 C++20 之前,可以用宏包裹
std::integral_constant
,使 throw
触发编译错误。
constexpr uint16_t operator "" _int(const char* s, std::size_t len)
{
return len == 2 ? s[0] | (s[1] << 8) : throw;
}
void test()
{
#define FORCE_CONSTANT(val) std::integral_constant<decltype(val), (val)>::value
FORCE_CONSTANT("AB"_int);
// FORCE_CONSTANT("ABC"_int); // error, expected compile-time constant expression
}
从 C++20 开始,事情变得容易了,用户定义的文字被允许是
string literal operator template
。 (cpref)
因此,以下代码将按您的预期工作。
template <size_t kCount>
struct template_str_buffer
{
using char_type = char;
consteval template_str_buffer(const char_type(&str)[kCount]) noexcept
{
for (size_t i = 0; i < kCount; ++i) {
data[i] = str[i];
}
}
char_type data[kCount];
constexpr static size_t count = kCount - sizeof(char_type);
};
template <template_str_buffer kStrBuf>
consteval uint16_t operator""_int()
{
static_assert(kStrBuf.count == 2);
return kStrBuf.data[0] | (kStrBuf.data[1] << 8);
}
void test()
{
"AB"_int;
// "ABC"_int; // static assertion failed
}
#include <iostream>
#include <cstdint>
using namespace std;
constexpr uint16_t operator "" _int(char const * s, size_t len)
{
return (len == 2) ? s[0] | (s[1] << 8) : throw "len must be 2!";
}
int main()
{
constexpr uint16_t i1 = "AB"_int; // OK
cout << i1 << endl; // outputs 16961
constexpr uint16_t i2 = "ABC"_int; // error
cout << i2 << endl;
return 0;
}
prog.cpp: In function ‘int main()’:
prog.cpp:13:29: in constexpr expansion of ‘operator""_int(((const char*)"ABC"), 3ul)’
prog.cpp:7:52: error: expression ‘<throw-expression>’ is not a constant-expression
return (len == 2) ? s[0] | (s[1] << 8) : throw "len must be 2!";
^~~~~~~~~~~~~~~~
不幸的是,作为评论发布是不切实际的。
除了使不良文字成为编译时错误之外还进行了修复:
throw
。#include <iostream>
#include <stdint.h>
#include <limits.h> // CHAR_BIT
using namespace std;
using Byte = unsigned char;
const int bits_per_byte = CHAR_BIT;
static_assert( bits_per_byte == 8, "!" );
constexpr auto operator "" _int( char const* s, std::size_t len )
-> uint16_t
{ return len == 2 ? Byte( s[0] ) | (Byte( s[1] ) << 8u) : throw "Bah!"; }
#define CHAR_PAIR( s ) static_cast<uint16_t>( sizeof( char[s ## _int] ) )
auto main()
-> int
{
CHAR_PAIR( "AB" ); // OK
CHAR_PAIR( "ABC" ); //! Doesn't compile as ISO C++.
}
使用 Visual C++ 这就是所需要的。
g++ 在这方面不太符合标准,因此对于该编译器添加选项
-Werror=vla
。
使用 g++,您也可以使用以下宏:
#define CHAR_PAIR( s ) []() constexpr { constexpr auto r = s##_int; return r; }()
这会提供更丰富的错误消息,但 Visual C++ 2017 不支持。
使用 C++20,您可以使用
consteval
和普通 assert
(或任何您喜欢的例外):
#include <iostream>
#include <cstdint>
#include <cassert>
consteval uint16_t operator "" _int(const char* s, size_t len)
{
assert(len == 2);
return s[0] | (s[1] << 8);
}
int main() {
std::cout << "AB"_int << std::endl;
//std::cout << "ABC"_int << std::endl; // compiler error
return 0;
}