我实际上忘记了这个 Q 并且我不知道我是否能找到我当时正在使用的原始代码,但我已经想出了如何在没有终止 NUL 字符的情况下存储一个字符串。
在 c++17 中,我能够constexpr
std::array<char, n>
用不包含尾随零的字符串填充 a 。
#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()
{
// Although I did this recursively, this could have also been done iteratively.
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
“你好”没有任何终止 NUL 字符。见Demo。
将第一行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
Demo
我也可以把它变成一个类型:
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)>());
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo
现在我可以多按摩一下,让它进入我上面要求的状态。要求是将字符串存储为非 NULL 终止,但也要在 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);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo
请注意,我现在能想到将字符串放入模板参数的唯一原因是将字符串作为 a 传输constexpr
,这可能有一些有趣的原因,例如允许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;
}
这导致与我的第一个示例相同的 asm 输出。
Demo
坦率地说,我很惊讶它确实有效。
编辑 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.
Demo
当然,对于 c++11 的这些工作,constexpr
必须将函数转换为递归三元版本。