问题 constexpr连接两个或多个char字符串


我正在尝试创建一个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都给出错误,表明没有匹配的函数 concat 在 - 的里面 decltype 表达式可以找到。

铛:

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 功能模板不是候选者 concat 在里面 decltype 表达。仅考虑第一个模板。为什么这样,我该如何解决这个问题?

编辑: 有关原因的相关问题 decltype 不能递归使用

使用具有可变参数模板函数的decltype的尾随返回类型


4463
2018-02-24 23:36


起源

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


答案:


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

摆脱了递归 - 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("");
}

实例


12
2018-02-25 00:15



如果你愿意的话我会对此有一个跟进问题! stackoverflow.com/questions/39199564/... - Short
该示例无法为Visual Studio 2015编译。........ \ src \ tsd.nav.sdk.mapdisplay.rmw \ app \ rmw_test \ test2.cpp(30):错误C2059:Syntaxfehler:“。 ..“(更多关注)想法? - user5024425
@user MSVC很穷 decltype 支持。我会尝试,在clsng / gcc上,接受我的代码并移动 length_t  decltype 别名并将它们冒泡到它们使用的平台上 length 在尾随返回类型中直接使用。 - Yakk - Adam Nevraumont
clang++ 上 coliru 失败的长错误列表,其余的消息 fatal error: too many errors emitted, stopping now [-ferror-limit=] 20 errors generated. - kyb
你能否在代码中添加注释来解释每个元素的作用。 - kyb