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

新手上路,请多包涵

能够在 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 ,字符串化,数组下标等)。但是,我没有时间(或足够的兴趣)来实现这样的宏 =)

原文由 void-pointer 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 713
2 个回答

我还没有看到任何东西可以与 Scott Schurr 在 C++ Now 2012 上发表 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 的隐含版权。实施是在他的演示文稿的一张幻灯片上链接到上面。

更新 C++17

自从我发布这个答案以来的几年里, std::string_view 已经成为我们工具箱的一部分。以下是我将如何使用 string_view 重写上述内容:

 #include <string_view>

int
main()
{
    constexpr std::string_view my_string = "Hello, world!";
    static_assert(my_string.size() == 13);
    static_assert(my_string[4] == 'o');
    constexpr std::string_view my_other_string = my_string;
    static_assert(my_string == my_other_string);
    constexpr std::string_view world(my_string.substr(7, 5));
    static_assert(world == "world");
//  constexpr char x = world.at(5); // Does not compile because index is out of range!
}

原文由 Howard Hinnant 发布,翻译遵循 CC BY-SA 4.0 许可协议

您正在寻找的是 N3599 字符串的文字运算符模板。它是在 2013 年为 C++ 提出的,但在细节上 没有达成共识,也从未添加到标准中。

但是,GCC 和 Clang 支持它作为扩展。它允许您将字符串文字拆分为字符的模板参数包:

 // some template type to represent a string
template <char... chars>
struct TemplateString {
    static constexpr char value[] = { chars... };

    template <char... chars2>
    constexpr auto operator+(TemplateString<chars2...>) const {
        // compile-time concatenation, oh yeah!
        return TemplateString<chars..., chars2...>{};
    }
};

// a custom user-defined literal called by the compiler when you use your _suffix
template <typename CharType, CharType... chars>
constexpr auto operator""_tstr () {
    // since all the chars are constants here, you can do compile-time
    // processing with constexpr functions and/or template metaprogramming,
    // and then return whatever converted type you like
    return TemplateString<chars...>{};
}

// auto = TemplateString<'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'>
constexpr auto str = "Hello"_tstr + " world!"_tstr;
cout << str.value << endl;

作为后备,使用宏的技巧可以将您带到同一个地方(例如,如 Smilethax 的答案 所示)。

请注意,这是接受字符串文字并将其拆分为 constexpr 字符的 仅有 的两种方法:要么使用扩展,要么在调用站点使用宏骇客。

原文由 Boann 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题