constexpr,用于连接两个或多个字符字符串
constexpr to concatenate two or more char strings
我正试图制作一个constexpr函数,通过Xeo的以下答案来连接任意数量的char数组,Xeo连接了两个char数组。
https://stackoverflow.com/a/13294458/1128289
#include <array>
template<unsigned... Is> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};
template<unsigned N1, unsigned... I1, unsigned N2, unsigned... I2>
constexpr std::array<char const, N1+N2-1> concat(char const (&a1)[N1], char const (&a2)[N2], seq<I1...>, seq<I2...>){
return {{ a1[I1]..., a2[I2]... }};
}
template<unsigned N1, unsigned N2>
constexpr std::array<char const, N1+N2-1> concat(char const (&a1)[N1], char const (&a2)[N2]){
return concat(a1, a2, gen_seq<N1-1>{}, gen_seq<N2>{});
}
到目前为止我的尝试:
#include <iostream>
#include <array>
template<unsigned... Is> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};
template<unsigned N1, unsigned... I1, unsigned N2, unsigned... I2>
constexpr const std::array<char, N1+N2-1>
concat_impl(
const char (&a1)[N1], const char (&a2)[N2], seq<I1...>, seq<I2...>)
{
return {{ a1[I1]..., a2[I2]... }};
}
template<unsigned N1, unsigned N2>
constexpr const std::array<char, N1+N2-1>
concat(const char (&a1)[N1], const char (&a2)[N2])
{
return concat_impl(a1, a2, gen_seq<N1-1>{}, gen_seq<N2>{});
}
template<unsigned N1, unsigned N2, class... Us>
constexpr auto
concat(const char(&a1)[N1], const char(&a2)[N2], const Us&... xs)
-> std::array<char, N1 + decltype(concat(a2, xs...))::size() - 1>
{
return concat(a1, concat(a2, xs...));
}
int main()
{
auto const s = concat("hi ", "there!");
std::cout << s.data() << std::endl;
// compile error:
auto const t = concat("hi ", "there ", "how ", "are ", "you?");
std::cout << t.data() << std::endl;
}
gcc 4.9和clang 3.5都给出了错误,表明在decltype
表达式中找不到与concat
匹配的函数。
叮当声:
error: no matching function for call to 'concat'
auto const t = concat("hi ", "there ", "how ", "are ", "you?");
^~~~~~
ctconcat.cpp:105:16: note: candidate template ignored: substitution failure [with N1 = 4, N2 = 7, Us = <char [5], char [5], char [5]>]: no matching function for call to 'concat'
constexpr auto concat(const char(&a1)[N1], const char(&a2)[N2], const Us&... xs) -> std::array<char, N1 + decltype(concat(a2, xs...))::size() - 1>
^ ~~~~~~
ctconcat.cpp:62:43: note: candidate function template not viable: requires 2 arguments, but 5 were provided
constexpr const std::array<char, N1+N2-1> concat(const char (&a1)[N1], const char (&a2)[N2])
^
1 error generated.
gcc和clang的错误都表明第二个concat
函数模板不是decltype
表达式中concat
的候选者。只考虑第一个模板。为什么会这样?我该如何解决?
编辑:decltype
不能递归使用的相关问题
使用decltype和可变模板函数的尾随返回类型
template<size_t S>
using size=std::integral_constant<size_t, S>;
template<class T, size_t N>
constexpr size<N> length( T const(&)[N] ) { return {}; }
template<class T, size_t N>
constexpr size<N> length( std::array<T, N> const& ) { return {}; }
template<class T>
using length_t = decltype(length(std::declval<T>()));
constexpr size_t sum_string_sizes() { return 0; }
template<class...Ts>
constexpr size_t sum_string_sizes( size_t i, Ts... ts ) {
return (i?i-1:0) + sum_sizes(ts...);
}
然后
template
template<unsigned N1, unsigned N2, class... Us>
constexpr auto
concat(const char(&a1)[N1], const char(&a2)[N2], const Us&... xs)
-> std::array<char, sum_string_sizes( N1, N2, length_t<Us>::value... )+1 >
{
return concat(a1, concat(a2, xs...));
}
消除了-CCD_ 7中的递归。
以下是使用上述方法的完整示例:
template<size_t S>
using size=std::integral_constant<size_t, S>;
template<class T, size_t N>
constexpr size<N> length( T const(&)[N] ) { return {}; }
template<class T, size_t N>
constexpr size<N> length( std::array<T, N> const& ) { return {}; }
template<class T>
using length_t = decltype(length(std::declval<T>()));
constexpr size_t string_size() { return 0; }
template<class...Ts>
constexpr size_t string_size( size_t i, Ts... ts ) {
return (i?i-1:0) + string_size(ts...);
}
template<class...Ts>
using string_length=size< string_size( length_t<Ts>{}... )>;
template<class...Ts>
using combined_string = std::array<char, string_length<Ts...>{}+1>;
template<class Lhs, class Rhs, unsigned...I1, unsigned...I2>
constexpr const combined_string<Lhs,Rhs>
concat_impl( Lhs const& lhs, Rhs const& rhs, seq<I1...>, seq<I2...>)
{
// the ' ' adds to symmetry:
return {{ lhs[I1]..., rhs[I2]..., ' ' }};
}
template<class Lhs, class Rhs>
constexpr const combined_string<Lhs,Rhs>
concat(Lhs const& lhs, Rhs const& rhs)
{
return concat_impl(
lhs, rhs,
gen_seq<string_length<Lhs>{}>{},
gen_seq<string_length<Rhs>{}>{}
);
}
template<class T0, class T1, class... Ts>
constexpr const combined_string<T0, T1, Ts...>
concat(T0 const&t0, T1 const&t1, Ts const&...ts)
{
return concat(t0, concat(t1, ts...));
}
template<class T>
constexpr const combined_string<T>
concat(T const&t) {
return concat(t, "");
}
constexpr const combined_string<>
concat() {
return concat("");
}
实例
使用C++17,解决方案变得非常简单(这是实时版本):
#include <initializer_list>
// we cannot return a char array from a function, therefore we need a wrapper
template <unsigned N>
struct String {
char c[N];
};
template<unsigned ...Len>
constexpr auto cat(const char (&...strings)[Len]) {
constexpr unsigned N = (... + Len) - sizeof...(Len);
String<N + 1> result = {};
result.c[N] = ' ';
char* dst = result.c;
for (const char* src : {strings...}) {
for (; *src != ' '; src++, dst++) {
*dst = *src;
}
}
return result;
}
// can be used to build other constexpr functions
template<unsigned L>
constexpr auto makeCopyright(const char (&author)[L]) {
return cat("xC2xA9 ", author);
}
constexpr char one[] = "The desert was the apotheosis of all deserts";
constexpr char two[] = "huge, standing to the sky";
constexpr auto three = cat(
cat(one, ", ", two).c, // can concatenate recursively
" ",
"for what looked like eternity in all directions."); // can use in-place literals
constexpr auto phrase = cat(
three.c, // can reuse existing cats
"n",
makeCopyright("Stephen King").c);
#include <cstdio>
int main() {
puts(phrase.c);
return 0;
}
由于C++20 std::copy
函数族是constexpr,因此您可以进一步简化@spiderface answer:
template<unsigned ...Len>
constexpr auto cat(const char (&...strings)[Len]) {
constexpr unsigned N = (... + Len) - sizeof...(Len);
std::array<char, N + 1> result = {};
result[N] = ' ';
auto it = result.begin();
(void)((it = std::copy_n(strings, Len-1, it), 0), ...);
return result;
}
请参阅https://godbolt.org/z/6xsMcqoor
解决旧的"部分"C++11编译器兼容性问题的另一种方法是完全取消使用decltype
。我对此进行了实验,并找到了一种利用类型系统的方法,类似于编译时图灵机的工作方式。该方法也不使用std::array
,并允许将结果分配给完全工作的constexpr const char*
变量。
-
介绍C++11:中的一些C++14特性
// Parameter pack helpers (similar to C++14) template<std::size_t ...> struct _index_sequence { using type = _index_sequence; }; template<std::size_t N, std::size_t ... S> struct gen_pack_sequence: gen_pack_sequence<N - 1, N - 1, S...> {}; template<std::size_t ... S> struct gen_pack_sequence<0, S...> : _index_sequence<S...> {}; template<std::size_t N> using _make_index_sequence = typename gen_pack_sequence<N>::type;
-
图灵机的几个
TypeList
模板,具有两个附加功能:template<typename List> struct PopFront; template<typename OldItem, typename ... Items> struct PopFront<TypeList<OldItem, Items...>> { typedef TypeList<Items...> type; }; template<typename ... T> struct ConcatStringList; template<typename S, typename ... First, typename ... Second, typename ... Tail> struct ConcatStringList<TypeList<First...>, TypeList<S, Second...>, Tail...> { typedef typename ReplaceItem<TypeList<First...>, GetSize<TypeList<First...>>::value - 1, S>::type first; typedef typename PopFront<TypeList<S, Second...>>::type second; typedef typename ConcatList<first, second>::type concat; typedef typename ConcatStringList<concat, Tail...>::type type; }; template<typename T> struct ConcatStringList<T> { typedef T type; };
-
包含数据的模板化结构:
template<char ... Chars> struct to_list { typedef typelist::TypeList<typelist::Char<Chars>...> type; }; template<char ... Chars> struct char_sequence { static constexpr char value[] = { Chars... }; typedef typename to_list<Chars...>::type list; template<template<char...> class Template> using param_pack = Template<Chars...>; }; template<char ... Chars> constexpr char char_sequence<Chars...>::value[]; template<char ... Chars> struct char_string: char_sequence<Chars..., ' '> {};
-
从TypeList转换:
template<typename CharList, typename = _make_index_sequence<typelist::GetSize<CharList>::value>> struct list_to_string; template<typename CharList, std::size_t ... Indices> struct list_to_string<CharList, _index_sequence<Indices...>> { static_assert(sizeof...(Indices) > 0 && typelist::GetItem<CharList, sizeof...(Indices) - 1>::type::value == 0, "missing null-termination"); typedef char_sequence<typelist::GetItem<CharList, Indices>::type::value...> type; };
-
定义constexpr文字的帮助程序:
constexpr std::size_t _strlen(char const* s, std::size_t count = 0) { return (*s == ' ') ? count : _strlen(s + 1, count + 1); } template<typename S, typename T> struct _struct_to_string; template<typename S, std::size_t ... Indices> struct _struct_to_string<S, _index_sequence<Indices...>> { typedef char_string<S::get()[Indices] ...> type; }; template<typename S> struct struct_to_string { typedef _make_index_sequence<_strlen(S::get())> indices; typedef typename _struct_to_string<S, indices>::type type; }; #define CONSTEXPR_STRING(name, s) struct name ## __struct { constexpr static const char* get() { return s; } }; typedef struct_to_string<name ## __struct>::type name
-
最后,字符串连接的结构:
template<typename ... Strings> struct concatenate { template<typename String> using list = typename String::list; typedef typename list_to_string<typename typelist::ConcatStringList<list<Strings>...>::type>::type type; };
可用性:
// ['year', 'month' and 'day' are not literals but somehow obtained earlier]
CONSTEXPR_STRING(dot, ".");
typedef concatenate<year, dot, month, dot, day>::type date;
constexpr const char* c_date = date::value;
它按照GCC 4.9.4进行编译。(我也可以确认它适用于GCC 4.8.5。)
- 如何读取单个字符并在输入两个字符序列时输出? 使用 while 循环和C++
- 在函数中连接两个字符,并在 C++ 中返回输出
- 舍入 QDecDouble 值,精度最多为两个字符
- 计算两个字符序列
- 在c++中连接两个字符
- 有没有办法我可以从 istringstream 中读取两个字符
- 如何查看两个字符变量是否相等
- 使用位于 C 字符串中的两个字符*获取子字符串
- 比较两个字符的问题
- 如何修复密码条目,它一次输入两个字符
- 字符串构造函数将两个字符* 放入另一个 std::string 在 C++14 中有效,但不适用于 C++17
- 将两个字符数组相互添加
- 在 c++ 中添加两个字符
- 如何删除QString的前两个字符
- 如何使用两个字符来控制 C++ 程序的流程
- 如何将两个字符或字符串数组附加到一个数组中
- 如何从两个字符之间的文件中提取整数
- 删除两个字符之间的文本
- std::setw() 将特殊字符视为两个字符
- 比较两个字符*是否相等