在 C++ 中方便地声明编译时字符串

能够在 C++ 编译时创建和操作字符串有几个有用的应用.尽管可以在 C++ 中创建编译时字符串,但该过程非常繁琐,因为该字符串需要声明为可变字符序列,例如

Being able to create and manipulate strings during compile-time in C++ has several useful applications. Although it is possible to create compile-time strings in C++, the process is very cumbersome, as the string needs to be declared as a variadic sequence of characters, e.g.

using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;

字符串连接、子字符串提取等操作可以轻松实现为对字符序列的操作.是否可以更方便地声明编译时字符串?如果没有,是否有工作中的提案可以方便地声明编译时字符串?

Operations such as string concatenation, substring extraction, and many others, can easily be implemented as operations on sequences of characters. Is it possible to declare compile-time strings more conveniently? If not, is there a proposal in the works that would allow for convenient declaration of compile-time strings?

理想情况下,我们希望能够如下声明编译时字符串:

Ideally, we would like to be able to declare compile-time strings as follows:

// Approach 1
using str1 = sequence<"Hello, world!">;

或者,使用用户定义的文字,

or, using user-defined literals,

// Approach 2
constexpr auto str2 = "Hello, world!"_s;

其中 decltype(str2) 将有一个 constexpr 构造函数.可以实现方法 1 的更混乱版本,利用您可以执行以下操作的事实:

where decltype(str2) would have a constexpr constructor. A messier version of approach 1 is possible to implement, taking advantage of the fact that you can do the following:

template <unsigned Size, const char Array[Size]>
struct foo;

然而,数组需要有外部链接,所以要使方法 1 起作用,我们必须这样写:

However, the array would need to have external linkage, so to get approach 1 to work, we would have to write something like this:

/* 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* 参数来指定返回类型:

Needless to say, this is very inconvenient. Approach 2 is actually not possible to implement. If we were to declare a (constexpr) literal operator, then how would we specify the return type? Since we need the operator to return a variadic sequence of characters, so we would need to use the const char* parameter to specify the return type:

constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */

这会导致编译错误,因为 s 不是 constexpr.尝试通过执行以下操作来解决此问题并没有多大帮助.

This results in a compile error, because s is not a constexpr. Trying to work around this by doing the following does not help much.

template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }

标准规定这种特定的文字运算符形式保留用于整数和浮点类型.虽然 123_s 会起作用,但 abc_s 不会.如果我们完全放弃用户定义的文字,而只使用常规的 constexpr 函数会怎样?

The standard dictates that this specific literal operator form is reserved for integer and floating-point types. While 123_s would work, abc_s would not. What if we ditch user-defined literals altogether, and just use a regular constexpr function?

template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */

和以前一样,我们遇到的问题是,数组现在是 constexpr 函数的参数,它本身不再是 constexpr 类型.

As before, we run into the problem that the array, now a parameter to the constexpr function, is itself no longer a constexpr type.

我相信应该可以定义一个 C 预处理器宏,它将字符串和字符串的大小作为参数,并返回由字符串中的字符组成的序列(使用 BOOST_PP_FOR,字符串化、数组下标等).但是,我没有时间(或足够的兴趣)来实现这样的宏 =)

I believe it should be possible to define a C preprocessor macro that takes a string and the size of the string as arguments, and returns a sequence consisting of the characters in the string (using BOOST_PP_FOR, stringification, array subscripts, and the like). However, I do not have the time (or enough interest) to implement such a macro =)

推荐答案

我没有看到任何东西可以与 Scott Schurr 的 str_const 在 C++ Now 2012 上发表.不过它确实需要 constexpr.

I haven't seen anything to match the elegance of Scott Schurr's str_const presented at C++ Now 2012. It does require constexpr though.

以下是您如何使用它,以及它可以做什么:

Here's how you can use it, and what it can do:

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!
}

没有比编译时范围检查更酷的了!

It doesn't get much cooler than compile-time range checking!

无论是使用还是实现,都没有宏.并且对字符串大小没有人为限制.我会在这里发布实现,但我尊重 Scott 的隐含版权.实现在他的演示文稿的一张幻灯片上,链接到上面.

Both the use, and the implementation, is free of macros. And there is no artificial limit on string size. I'd post the implementation here, but I'm respecting Scott's implicit copyright. The implementation is on a single slide of his presentation linked to above.

相关文章