无法创建字符串字面量类型

6

我想创建一个字符串字面量,用作模板参数。这会导致编译器陷入某种无限循环。问题在哪里,如何修复?

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

编辑: 请查看这个答案,它说明生成编译时切片类型是可行的。


当然支持这个,例如,slit<'a','b','c'>() 运行良好,我只需要一个生成函数。 - user1095108
@user1095108,你的问题在于*s ?:并不是一个编译时结构,据我所知。因为没有任何阻止你调用string s; cin >> s; make_slit(s.c_str()); - PeterT
那种情况应该是编译器错误,而不是无限循环。请注意,该函数是“constexpr”。 - user1095108
@user1095108,使用gcc有什么问题吗? - PeterT
@user1095108 嗯,可能无论 ?: 的真值如何,它都会被实例化,因为它是表达式的一部分。 - PeterT
显示剩余3条评论
2个回答

3

除去循环实例化,你无法将函数参数用作常量表达式,而模板参数需要常量表达式。这意味着以下内容也是不允许的:

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;

这个缺失的功能最近在Richard Smith的n3599: 字符串字面量运算符模板中被提出。不幸的是,我不知道该功能目前的状态。


3

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

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

编辑:用自定义字面值替换了constexpr strlen,它直接返回长度并消除了对C++1y松散constexpr函数的依赖。


2
你可以使用sizeof(str)-1代替_len - Jan Herrmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接