我曾经忘记了这个问题,并不确定能否找回当时的原始代码,但我已经想出如何存储一个没有终止NUL字符的字符串。
在c++17中,我能够使用一组不包含尾零的字符填充一个constexpr std :: array。
#include <array>
#include <cstdio>
constexpr size_t str_len(char const * x)
{
char const * begin = x;
while (*x) {
++x;
}
return x - begin;
}
constexpr auto var = "hello there";
template <size_t I, size_t Max>
constexpr auto fn()
{
if constexpr (I < Max) {
auto x = fn<I + 1, Max>();
x[I] = var[I];
return x;
}
else {
return std::array<char, Max>{};
}
}
int main()
{
auto x = fn<0, str_len(var)>();
printf("'%*.*s'\n", x.size(), x.size(), x.data());
return 0;
}
这将产生以下汇编代码:
.LC0:
.string "'%*.*s'\n"
main:
sub rsp, 24
mov edx, 11
mov esi, 11
movabs rax, 7526676540175443304 ; <<< hello there
mov QWORD PTR [rsp+5], rax
mov eax, 29285
lea rcx, [rsp+5]
mov edi, OFFSET FLAT:.LC0
mov WORD PTR [rsp+13], ax
xor eax, eax
mov BYTE PTR [rsp+15], 101
call printf
xor eax, eax
add rsp, 24
ret
是的,
7526676540175443304
是 "hello there" 字符串,没有任何终止的 NUL 字符。
请参见
演示。
将第一行代码放入
main()
的全局空间将导致该字符串位于全局 .text 段中。
.LC0:
.string "'%*.*s'\n"
main:
sub rsp, 8
mov ecx, OFFSET FLAT:x
mov edx, 11
xor eax, eax
mov esi, 11
mov edi, OFFSET FLAT:.LC0
call printf
xor eax, eax
add rsp, 8
ret
x: ; <<< hello there
.byte 104
.byte 101
.byte 108
.byte 108
.byte 111
.byte 32
.byte 116
.byte 104
.byte 101
.byte 114
.byte 101
最初的回答:
我也可以将其转换为类型:
演示
template <char x, typename...Ts>
struct X
{
};
constexpr int str_len(char const * x)
{
char const * begin = x;
while (*x) {
++x;
}
return x - begin;
}
constexpr auto var = "hello there";
template <int I>
constexpr auto fn()
{
if constexpr (I - 1 != 0)
return X<var[str_len(var) - I], decltype(fn<I - 1>())>{};
else
return X<var[str_len(var) - I], void>{};
}
int main()
{
decltype(nullptr)(fn<str_len(var)>());
return 0;
}
这给我输出:
最初的回答:
<source>:28:5: error: cannot convert 'X<'h', X<'e', X<'l', X<'l', X<'o', X<' ', X<'t', X<'h', X<'e', X<'r', X<'e', void> > > > > > > > > > >' to 'decltype(nullptr)' (aka 'nullptr_t') without a conversion operator
decltype(nullptr)(fn<str_len(var)>());
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"最初的回答"翻译成英文是 "Original Answer"。
以下为需要翻译的内容:
Demo
现在,我可以进一步修改它,以满足上面提出的要求。要求是将字符串存储为非NULL终止,但也要在c++0x中实现这一点,而这不是c++0x,因此我不会将其标记为答案。但我想把它放出来。
编辑
似乎gnu和clang也有一个扩展,允许将字符串放入模板类型中:
template <char...Cs>
struct chars {};
template <typename T, T...Xs>
chars<Xs...> operator""_xxx() {
return {};
}
int main()
{
decltype(nullptr)("hello there"_xxx);
return 0;
}
"最初的回答"应该是:
这将输出:
。
<source>:5:14: warning: string literal operator templates are a GNU extension [-Wgnu-string-literal-operator-template]
chars<Xs...> operator""_xxx() {
^
<source>:11:5: error: cannot convert 'chars<'h', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'e', 'r', 'e'>' to 'decltype(nullptr)' (aka 'nullptr_t') without a conversion operator
decltype(nullptr)("hello there"_xxx);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
演示
请注意,将字符串放入模板参数的唯一原因是将字符串作为constexpr
传递,这可能有一些有趣的原因,例如允许基于传递的字符串改变所述constexpr
函数的返回类型。这有一些有趣的可能性。
另外需要注意的是:不可能直接将字符串传递给constexpr
函数并使其改变返回类型,因为作为参数时它不再是constexpr
,这有点烦人。唯一的方法是将其声明为外部constexpr
变量,并从函数内引用该外部constexpr
变量,就像我第二个示例中所示。
编辑2
事实证明,虽然您不能直接将某些内容作为constexpr
值传递,但可以传递一个lambda,它将作为constexpr
函数工作。
#include <array>
#include <cstdio>
constexpr size_t str_len(char const * x)
{
char const * begin = x;
while (*x) {
++x;
}
return x - begin;
}
template <size_t I = 0, typename FN>
constexpr auto fn2(FN str) {
constexpr auto Max = str_len(str());
if constexpr (I < Max) {
auto x = fn2<I + 1>(str);
x[I] = str()[I];
return x;
}
else {
return std::array<char, Max>{};
}
}
auto x = fn2<>([]{ return "hello there"; });
int main()
{
printf("'%*.*s'\n", x.size(), x.size(), x.data());
return 0;
}
这将产生与我第一个示例相同的汇编输出。
演示
坦率地说,我很惊讶它实际上起作用了。
编辑3
鉴于我已经找到了如何传递
constexpr
字符串,现在我可以创建一个非递归类型:
#include <utility>
constexpr std::size_t str_len(char const * x)
{
char const * begin = x;
while (*x) {
++x;
}
return x - begin;
}
template <char...> struct c{};
template <typename FN, std::size_t...Is>
constexpr auto string_to_type_impl(FN str, std::index_sequence<Is...>)
{
return c<str()[Is]...>{};
}
template <typename FN>
constexpr auto string_to_type(FN str)
{
constexpr auto Max = str_len(str());
return string_to_type_impl(str, std::make_index_sequence<Max>{});
}
int main()
{
std::nullptr_t(string_to_type([]{ return "hello there"; }));
return 0;
}
使用以下输出结果:
最初的回答:
<source>:29:5: error: cannot convert 'c<'h', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'e', 'r', 'e'>' to 'std::nullptr_t' (aka 'nullptr_t') without a conversion operator
std::nullptr_t(string_to_type([]{ return "hello there"; }));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
当然,对于这些使用c++11的工作,
constexpr
函数必须转换为递归三元版本。
演示