简体   繁体   English

constexpr 连接两个或多个字符字符串

[英]constexpr to concatenate two or more char strings

I'm trying to make a constexpr function that will concatenate an arbitrary number of char arrays by working from the following answer by Xeo, which concatenates two char arrays.我正在尝试创建一个 constexpr 函数,该函数将通过 Xeo 的以下答案来连接任意数量的 char 数组,它连接了两个 char 数组。

https://stackoverflow.com/a/13294458/1128289 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>{});
}

My attempt thus far:到目前为止我的尝试:

#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;
}

Both gcc 4.9 and clang 3.5 give errors indicating that no function matching the concat inside the decltype expression can be found. gcc 4.9 和 clang 3.5 都会给出错误,表明在decltype表达式中找不到与concat匹配的函数。

clang:铛:

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.

The errors from gcc and clang both indicate that the second concat function template is not a candidate for the concat in the decltype expression. gcc 和 clang 的错误都表明第二个concat函数模板不是decltype表达式中concat的候选者。 Only the first template is considered.只考虑第一个模板。 Why is that and how do I fix this?为什么会这样,我该如何解决?

Edit: Relevant question on why decltype can't be used recursively编辑:关于为什么不能递归使用decltype相关问题

trailing return type using decltype with a variadic template function 使用带有可变参数模板函数的 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...);
}

then然后

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...));
}

which gets rid of the recursion-in- decltype .它摆脱了decltype中的递归。


Here is a full example using the above approach:这是使用上述方法的完整示例:

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 '\0' adds to symmetry:
  return {{ lhs[I1]..., rhs[I2]..., '\0' }};
}

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("");
}

live example活生生的例子

With C++17 the solution becomes very simple ( here's the live version ):使用 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] = '\0';

  char* dst = result.c;
  for (const char* src : {strings...}) {
    for (; *src != '\0'; 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("\xC2\xA9 ", 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM