如何在编译时从 constexpr char 数组组成字符串文字?
How to compose a string literal from constexpr char arrays at compile time?
我正在尝试创建一个将 const char 数组连接到一个数组的 constexpr 函数。我的目标是通过引用始终连接两个 const char*
的专用变量模板 join
递归地执行此操作。但是编译器不喜欢它并抛出一条我无法落后的错误消息。
我已经查看了 this 主题,但不幸的是它没有直接的答案。
Code:
#include <type_traits>
#include <cstdio>
#include <iostream>
constexpr auto size(const char*s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <const char* S1, typename, const char* S2, typename>
struct join_impl;
template <const char* S1, int... I1, const char* S2, int... I2>
struct join_impl<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>>
{
static constexpr const char value[]{ S1[I1]..., S2[I2]..., 0 };
};
template <const char* S1, const char* S2>
constexpr auto join
{
join_impl<S1, std::make_index_sequence<size(S1)>, S2, std::make_index_sequence<size(S2)>>::value
};
template <const char* S1, const char* S2, const char*... S>
struct join_multiple
{
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
};
template <const char* S1, const char* S2>
struct join_multiple<S1, S2>
{
static constexpr const char* value = join<S1, S2>;
};
constexpr const char a[] = "hello";
constexpr const char b[] = "world";
constexpr const char c[] = "how is it going?";
int main()
{
// constexpr size_t size = 100;
// char buf[size];
// lw_ostream{buf, size};
std::cout << join_multiple<a, b, c>::value << std::endl;
}
错误:
<source>:33:82: error: qualified name refers into a specialization of variable template 'join'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
<source>:25:16: note: variable template 'join' declared here
constexpr auto join
^
<source>:33:34: error: default initialization of an object of const type 'const char *const'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
^
= nullptr
2 errors generated.
ASM generation compiler returned: 1
<source>:33:82: error: qualified name refers into a specialization of variable template 'join'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
<source>:25:16: note: variable template 'join' declared here
constexpr auto join
^
<source>:33:34: error: default initialization of an object of const type 'const char *const'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
^
= nullptr
2 errors generated.
Execution build compiler returned:
我错过了什么?
这里有两个问题。
首先,join
是一个模板变量,所以它不包含so-called value_type
,它本身就是一个值,所以你的join_multiple
应该是
template <const char* S1, const char* S2, const char*... S>
struct join_multiple {
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>;
};
其次也是次要的,index_sequence
的整数类型是size_t
而不是int
,所以join_impl
的偏特化应该是(这个不是必须的) , 但使用 size_t
以外的类型将导致 GCC reject it incorrectly)
template <const char* S1, size_t... I1, const char* S2, size_t... I2>
struct join_impl<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>> {
static constexpr const char value[]{ S1[I1]..., S2[I2]..., 0 };
};
作为替代方案,为了避免构建临时 char 数组,您可以使用类型(char 序列)并仅在最后创建 char 数组变量,例如:
constexpr auto size(const char*s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <const char* S, typename Seq = std::make_index_sequence<size(S)>>
struct as_sequence;
template <const char* S, std::size_t... Is>
struct as_sequence<S, std::index_sequence<Is...>>
{
using type = std::integer_sequence<char, S[Is]...>;
};
template <typename Seq>
struct as_string;
template <char... Cs1>
struct as_string<std::integer_sequence<char, Cs1...>>
{
static constexpr const char c_str[] = {Cs1..., '[=10=]'};
};
template <typename Seq1, typename Seq2, typename... Seqs>
struct join_seqs
{
using type = typename join_seqs<typename join_seqs<Seq1, Seq2>::type, Seqs...>::type;
};
template <char... Cs1, char... Cs2>
struct join_seqs<std::integer_sequence<char, Cs1...>, std::integer_sequence<char, Cs2...>>
{
using type = std::integer_sequence<char, Cs1..., Cs2...>;
};
template <const char*... Ptrs>
const auto join =
as_string<typename join_seqs<typename as_sequence<Ptrs>::type...>::type>::c_str;
我正在尝试创建一个将 const char 数组连接到一个数组的 constexpr 函数。我的目标是通过引用始终连接两个 const char*
的专用变量模板 join
递归地执行此操作。但是编译器不喜欢它并抛出一条我无法落后的错误消息。
我已经查看了 this 主题,但不幸的是它没有直接的答案。
Code:
#include <type_traits>
#include <cstdio>
#include <iostream>
constexpr auto size(const char*s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <const char* S1, typename, const char* S2, typename>
struct join_impl;
template <const char* S1, int... I1, const char* S2, int... I2>
struct join_impl<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>>
{
static constexpr const char value[]{ S1[I1]..., S2[I2]..., 0 };
};
template <const char* S1, const char* S2>
constexpr auto join
{
join_impl<S1, std::make_index_sequence<size(S1)>, S2, std::make_index_sequence<size(S2)>>::value
};
template <const char* S1, const char* S2, const char*... S>
struct join_multiple
{
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
};
template <const char* S1, const char* S2>
struct join_multiple<S1, S2>
{
static constexpr const char* value = join<S1, S2>;
};
constexpr const char a[] = "hello";
constexpr const char b[] = "world";
constexpr const char c[] = "how is it going?";
int main()
{
// constexpr size_t size = 100;
// char buf[size];
// lw_ostream{buf, size};
std::cout << join_multiple<a, b, c>::value << std::endl;
}
错误:
<source>:33:82: error: qualified name refers into a specialization of variable template 'join'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
<source>:25:16: note: variable template 'join' declared here
constexpr auto join
^
<source>:33:34: error: default initialization of an object of const type 'const char *const'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
^
= nullptr
2 errors generated.
ASM generation compiler returned: 1
<source>:33:82: error: qualified name refers into a specialization of variable template 'join'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
<source>:25:16: note: variable template 'join' declared here
constexpr auto join
^
<source>:33:34: error: default initialization of an object of const type 'const char *const'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
^
= nullptr
2 errors generated.
Execution build compiler returned:
我错过了什么?
这里有两个问题。
首先,join
是一个模板变量,所以它不包含so-called value_type
,它本身就是一个值,所以你的join_multiple
应该是
template <const char* S1, const char* S2, const char*... S>
struct join_multiple {
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>;
};
其次也是次要的,index_sequence
的整数类型是size_t
而不是int
,所以join_impl
的偏特化应该是(这个不是必须的) , 但使用 size_t
以外的类型将导致 GCC reject it incorrectly)
template <const char* S1, size_t... I1, const char* S2, size_t... I2>
struct join_impl<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>> {
static constexpr const char value[]{ S1[I1]..., S2[I2]..., 0 };
};
作为替代方案,为了避免构建临时 char 数组,您可以使用类型(char 序列)并仅在最后创建 char 数组变量,例如:
constexpr auto size(const char*s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <const char* S, typename Seq = std::make_index_sequence<size(S)>>
struct as_sequence;
template <const char* S, std::size_t... Is>
struct as_sequence<S, std::index_sequence<Is...>>
{
using type = std::integer_sequence<char, S[Is]...>;
};
template <typename Seq>
struct as_string;
template <char... Cs1>
struct as_string<std::integer_sequence<char, Cs1...>>
{
static constexpr const char c_str[] = {Cs1..., '[=10=]'};
};
template <typename Seq1, typename Seq2, typename... Seqs>
struct join_seqs
{
using type = typename join_seqs<typename join_seqs<Seq1, Seq2>::type, Seqs...>::type;
};
template <char... Cs1, char... Cs2>
struct join_seqs<std::integer_sequence<char, Cs1...>, std::integer_sequence<char, Cs2...>>
{
using type = std::integer_sequence<char, Cs1..., Cs2...>;
};
template <const char*... Ptrs>
const auto join =
as_string<typename join_seqs<typename as_sequence<Ptrs>::type...>::type>::c_str;