不能创建字符串文本类型

can't make a string literal type

本文关键字:类型 文本 字符串 创建 不能      更新时间:2023-10-16

我想做一个字符串字面值,我可以用作模板参数。它使编译器陷入某种无限循环。问题和解决方法是什么?

template <char...> struct slit { };
template <typename ...A>
constexpr auto make_slit(char const* const s, A const ...args)
{
  return *s ? make_slit(s + 1, *s, args...) : slit<args...>();
}
int main()
{
  auto const tmp_(make_slit("slit"));
  return 0;
}

强制性错误(含clang++ -std=c++1y):

t.cpp:4:16: fatal error: recursive template instantiation exceeded maximum depth of 256
constexpr auto make_slit(char const* const s, A const ...args)
               ^
t.cpp:6:15: note: in instantiation of function template specialization 'make_slit<char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char,
      char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char, char>' requested here
  return *s ? make_slit(s + 1, *s, args...) : slit<args...>();

编辑:看看这个答案,生成编译时分隔类型是可能的。

如果不考虑循环实例化,则无法实现所需的功能,因为函数形参不能用作常量表达式,而这是模板实参所必需的。这意味着以下内容也不允许:

template <typename... A>
constexpr auto make_slit(A const... args)
{
  return slit<args...>();
}
// error
make_slit('a');
如果你觉得这很奇怪,请记住constexpr函数是一个特性,它允许一些函数也可以在常量表达式中使用。然而,你的不是一般的:
char c;
std::cin >> c;
// what is the result type?
make_slit(c);
然而,我应该注意到,在设计字面值字符串操作符的过程中,有人建议允许函数模板形式(很像整数和浮点字面值),这将实现您所需要的:
// allowed...
template<char... Cs>
constexpr slit<Cs...> operator"" _slit()
{ return {}; }
// ...but only for
auto constexpr s = 123_slit;
auto constexpr t = 12.3_slit;
// ... and not
auto constexpr u = "abc"_slit;

这个缺失的功能最近在2013年由Richard Smith的n3599提出:字符串的文字操作符模板。遗憾的是,我不知道该功能的当前状态。

您可以在这里找到将字符串字元扩展为参数包的解决方案

#include <iostream>
// c++14 has it http://en.cppreference.com/w/cpp/utility/integer_sequence
namespace detail {
    template <int... Is> struct seq { };
    template <int N, int... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {  };
    template <int... Is> struct gen_seq<0, Is...> : seq<Is...> { };
}
constexpr size_t operator"" _len ( const char*, size_t len ){ return len; }
template < char... val > struct slit {};
#define EXPAND_STRING( type_name, str ) 
template <int... Is> slit< str[Is]...> make_##type_name( detail::seq<Is...> ); 
using type_name = decltype( make_##type_name( detail::gen_seq<str##_len>{} ) );
using Manual = slit< 'b', 'a', 'z'>; 
EXPAND_STRING( MyFoo, "foo bar baz");
EXPAND_STRING( MyBar, "bla bli blo blu");
inline std::ostream& operator<<( std::ostream& os, slit<> ) { return os; }
template < char first, char... chars >
std::ostream& operator<<( std::ostream& os, slit<first,chars...> ) {
    return os << first << " " << slit<chars...>{};
}
int main() {
    std::cout << Manual{} << "n";
    std::cout << MyFoo{} << "n";
    std::cout << MyBar{} << "n";
}

EDIT:将constexpr字符串替换为自定义文本,它直接返回长度,并使用c++ 1y的轻松constexpr函数消除依赖。