在C++中,能够在编译时创建和操作字符串有几个有用的应用程序。尽管可以在C++中创建编译时字符串,但这个过程非常繁琐,因为字符串需要声明为可变的字符序列,例如
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
字符串连接、子字符串提取等操作可以很容易地实现为对字符序列的操作。是否可以更方便地声明编译时字符串?如果没有,工作中有没有一项建议可以方便地声明编译时字符串?
为什么现有的方法会失败
理想情况下,我们希望能够像下面这样声明编译时字符串:
// Approach 1
using str1 = sequence<"Hello, world!">;
或者,使用用户定义的文字,
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
其中decltype(str2)
会有一个constexpr
构造函数。可以实现方法1的一个更混乱的版本,利用您可以执行以下操作的事实:
template <unsigned Size, const char Array[Size]>
struct foo;
但是,该数组需要有外部链接,因此要使方法1起作用,我们必须编写如下代码:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
不用说,这是非常不方便的。方法2实际上是不可能实现的。如果我们声明一个(constexpr
)文字运算符,那么我们如何指定返回类型呢?因为我们需要操作符返回一个可变的字符序列,所以我们需要使用const char*
参数来指定返回类型:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
这会导致编译错误,因为s
不是constexpr
。尝试通过执行以下操作来解决此问题不会有太大帮助。
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
标准规定,这种特定的文字运算符形式是为整数和浮点类型保留的。虽然123_s
可以工作,但abc_s
不能。如果我们完全抛弃用户定义的文字,只使用常规的constexpr
函数会怎么样?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
和前面一样,我们遇到了这样的问题:数组现在是constexpr
函数的参数,它本身不再是constexpr
类型。
我认为应该可以定义一个C预处理器宏,它将字符串和字符串的大小作为参数,并返回由字符串中的字符组成的序列(使用BOOST_PP_FOR
、字符串化、数组下标等)。但是,我没有时间(或足够的兴趣)来实现这样的宏=)
发布于 2013-04-07 23:02:38
我还没有看到任何可以与C++ Now 2012上展示的Scott Schurr's str_const
的优雅相媲美的东西。不过,它确实需要constexpr
。
下面是你如何使用它,以及它能做什么:
int
main()
{
constexpr str_const my_string = "Hello, world!";
static_assert(my_string.size() == 13, "");
static_assert(my_string[4] == 'o', "");
constexpr str_const my_other_string = my_string;
static_assert(my_string == my_other_string, "");
constexpr str_const world(my_string, 7, 5);
static_assert(world == "world", "");
// constexpr char x = world[5]; // Does not compile because index is out of range!
}
没有比编译时范围检查更酷的了!
无论是使用还是实现,都没有宏。而且对字符串的大小没有人为的限制。我会在这里发布实现,但我尊重Scott的隐含版权。实现在他的演示文稿的一张幻灯片上链接到上面。
发布于 2013-04-10 05:24:01
我认为应该可以定义一个C预处理器宏,它将字符串和字符串的大小作为参数,并返回由字符串中的字符组成的序列(使用BOOST_PP_FOR、字符串化、数组下标等)。但是,我没有时间(或足够的兴趣)来实现这样的宏
使用非常简单的宏和一些C++11功能,可以在不依赖boost的情况下实现这一点:
通用常量expressions
(后两者在这里不是严格要求)
我们需要能够使用用户提供的从0到N的索引来实例化可变模板--这是一个非常有用的工具,例如可以将元组扩展为可变模板函数的参数(参见
"unpacking" a tuple to call a matching function pointer)
命名空间variadic_toolbox { template类meta_functor,未签名...indices>结构apply_range { typedef apply_range::result result;};template类meta_functor,无符号...indices> struct apply_range<0,meta_functor,indices...> { typedef meta_functor::result result;};}
命名空间compile_time { template结构字符串{ static constexpr const char charssizeof...(字符串)+1= {str...,'\0'};};template constexpr const char string::charssizeof...(str)+1;}
命名空间compile_time { template结构string_builder { template f string结果;};};}#定义CSTRING(string_literal) \ []{ \ struct constexpr_string_type { const char * chars = string_literal;};\ return variadic_toolbox::apply_range::produce>::result{};\ }()
一个简单的拼接演示展示了其用法:
namespace compile_time
{
template<char... str0, char... str1>
string<str0..., str1...> operator*(string<str0...>, string<str1...>)
{
return {};
}
}
int main()
{
auto str0 = CSTRING("hello");
auto str1 = CSTRING(" world");
std::cout << "runtime concat: " << str_hello.chars << str_world.chars << "\n <=> \n";
std::cout << "compile concat: " << (str_hello * str_world).chars << std::endl;
}
发布于 2013-04-07 23:00:25
编辑:正如Howard Hinnant (和我在我对OP的评论中所指出的)所指出的,您可能不需要将字符串的每个字符都作为单个模板参数的类型。如果你确实需要这个,下面有一个无宏的解决方案。
我在编译时尝试处理字符串时发现了一个技巧。它需要引入“模板字符串”之外的另一种类型,但是在函数中,你可以限制这种类型的作用域。
它不使用宏,而是使用一些C++11特性。
#include <iostream>
// helper function
constexpr unsigned c_strlen( char const* str, unsigned count = 0 )
{
return ('\0' == str[0]) ? count : c_strlen(str+1, count+1);
}
// destination "template string" type
template < char... chars >
struct exploded_string
{
static void print()
{
char const str[] = { chars... };
std::cout.write(str, sizeof(str));
}
};
// struct to explode a `char const*` to an `exploded_string` type
template < typename StrProvider, unsigned len, char... chars >
struct explode_impl
{
using result =
typename explode_impl < StrProvider, len-1,
StrProvider::str()[len-1],
chars... > :: result;
};
// recursion end
template < typename StrProvider, char... chars >
struct explode_impl < StrProvider, 0, chars... >
{
using result = exploded_string < chars... >;
};
// syntactical sugar
template < typename StrProvider >
using explode =
typename explode_impl < StrProvider,
c_strlen(StrProvider::str()) > :: result;
int main()
{
// the trick is to introduce a type which provides the string, rather than
// storing the string itself
struct my_str_provider
{
constexpr static char const* str() { return "hello world"; }
};
auto my_str = explode < my_str_provider >{}; // as a variable
using My_Str = explode < my_str_provider >; // as a type
my_str.print();
}
https://stackoverflow.com/questions/15858141
复制相似问题