我有一些辅助方法,它们通过引用接受指针,如果满足某些条件,则将其向前推进。这是一个例子:
char32_t readCodePoint(const char8_t *¤t, const char8_t *end) {
assert((current < end) && u8"At least one byte of input must be available");
char8_t leadCharacter = *current;
if(leadCharacter < 128) {
++current;
return static_cast<char32_t>(leadCharacter);
} else if((leadCharacter & 0xE0) == 0xC0) {
// ...code for 2, 3 and 4 byte code points omitted...
// (all increment 'current' accordingly and respect 'end')
}
// Sequence invalid or truncated
return char32_t(-1);
}
现在我可以在
const char8_t *
指针上使用该方法,但它会强制指针变为 const
。以下将导致编译错误:
void collapseDuplicateWhitespace(std::u8string &utf8String) {
char8_t *current = utf8String.data();
char8_t *end = current + utf8String.end();
while(current < end) {
// ERROR: 'current' is a 'char8_t *' not a 'const char8_t *'
char32_t codePoint = readCodePoint(current, end);
// ... other logic ommitted ...
}
// ... resize string to new, shorter length ...
}
显然,
const_cast
的结果也不能直接作为引用传递,因此它会在调用方产生一些非常难看的代码,甚至可能在发布版本中引入别名问题。
是否有一种优雅的(最好是可读的)方式可以更改此方法以将
const char8_t *&
和 char8_t *&
作为 current
的类型?
唾手可得的解决方案是一个模板:
template <typename T, typename U>
concept possibly_const = std::same_as<T, const U> || std::same_as<T, U>;
char32_t readCodePoint(possibly_const<char8_t> auto *¤t, const char8_t *end) {
// ...
}
另一个解决方案是重写代码以使用索引而不是指针,这消除了这里的常量问题:
char32_t readCodePoint(std::span<const char8_t> data, std::ptrdiff_t &index) {
// ...
}