【问题标题】:constexpr to concatenate two or more char stringsconstexpr 连接两个或多个字符字符串
【发布时间】:2015-02-24 23:36:37
【问题描述】:

我正在尝试创建一个 constexpr 函数,该函数将通过 Xeo 的以下答案连接任意数量的 char 数组,该答案连接两个 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不能递归使用的相关问题

trailing return type using decltype with a variadic template function

【问题讨论】:

  • 我错过了什么,但"hi " "there!" 有什么问题,它也会连接字符串?
  • 据我所知,这是一个基本的名称查找问题:在 trailing-return-type 中,函数(模板)尚未声明,因此无法通过纯非限定查找找到.不过,ADL 可以找到它。
  • @dyp 啊,有道理。 ADL能找到吗?如何?等等,不……我又糊涂了……问题发生在实例化时,而不是声明时。
  • @NeilKirk 这适用于无法选择的情况。直到编译时才知道字符串(与模板的作者身份时间相反)。
  • ADL 找不到 concat,因为该函数模板不与全局命名空间关联,例如通过其函数参数或模板参数的类型。为了在依赖模板参数的调用中查找函数名称,ADL 也将从实例化点开始执行,即在声明完成并且可以找到函数模板(其名称)之后。例如,尝试添加一个模板参数,该参数填充了在全局命名空间中声明的类类型。参见例如stackoverflow.com/a/21815838

标签: c++ templates c++11 constexpr


【解决方案1】:
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...));
}

它摆脱了递归-in-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 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

【讨论】:

  • 我有一个关于这个的后续问题,如果你愿意的话! stackoverflow.com/questions/39199564/…
  • 该示例无法为 Visual Studio 2015 编译。........\src\tsd.nav.sdk.mapdisplay.rmw\app\rmw_test\test2.cpp(30) : 错误 C2059: Syntaxfehler: "..." (更多关注) 想法?
  • @user MSVC 对decltype 的支持很差。我会尝试,在 clsng/gcc 上,获取我的代码并移动 length_t decltype 别名并将它们冒泡到它们所使用的位置,因此 length 在尾随返回类型中直接使用。
  • clang++ on coliru 失败,错误列表很长,消息的其余部分 fatal error: too many errors emitted, stopping now [-ferror-limit=] 20 errors generated.
  • 您能否在代码中添加 cmets 以解释每个元素的作用。
【解决方案2】:

使用 C++17 解决方案变得非常简单 (here's the live version):

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-05
    • 1970-01-01
    • 2011-08-29
    • 2020-08-19
    • 2018-03-10
    • 2012-02-02
    相关资源
    最近更新 更多