【问题标题】:can't make a string literal type无法创建字符串文字类型
【发布时间】:2014-03-24 08:04:11
【问题描述】:

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

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

编辑:查看this 答案,生成编译时隙类型是可能的。

【问题讨论】:

  • C++ 似乎不支持这一点。检查this question
  • 当然支持这个,比如slit&lt;'a', 'b', 'c'&gt;()就可以了,我只需要一个生成函数。
  • @user1095108 您的问题是,据我所知,*s ?: 不是编译时构造。因为没有什么能阻止你打电话给string s; cin &gt;&gt; s; make_slit(s.c_str());
  • 在这方面应该存在编译器错误,而不是无限循环。注意函数是constexpr
  • @user1095108 有 gcc

标签: c++ constexpr c++14


【解决方案1】:

抛开循环实例化,你无法实现你想要的,因为函数参数不能用作模板参数所必需的常量表达式。意味着以下内容也是不允许的:

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: Literal operator templates for strings 提出的。不幸的是,我不知道该功能的当前状态是什么。

【讨论】:

    【解决方案2】:

    您可以找到将字符串文字扩展为参数包here的解决方案

    #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 函数删除依赖关系。

    【讨论】:

    • 你可以使用 sizeof(str)-1 而不是使用 _len
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多