首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在C++中方便地声明编译时字符串

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

Stack Overflow用户
提问于 2013-04-07 10:10:17
回答 19查看 84.4K关注 0票数 151

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

代码语言:javascript
复制
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;

字符串连接、子字符串提取等操作可以很容易地实现为对字符序列的操作。是否可以更方便地声明编译时字符串?如果没有,工作中有没有一项建议可以方便地声明编译时字符串?

为什么现有的方法会失败

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

代码语言:javascript
复制
// Approach 1
using str1 = sequence<"Hello, world!">;

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

代码语言:javascript
复制
// Approach 2
constexpr auto str2 = "Hello, world!"_s;

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

代码语言:javascript
复制
template <unsigned Size, const char Array[Size]>
struct foo;

但是,该数组需要有外部链接,因此要使方法1起作用,我们必须编写如下代码:

代码语言:javascript
复制
/* 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*参数来指定返回类型:

代码语言:javascript
复制
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */

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

代码语言:javascript
复制
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }

标准规定,这种特定的文字运算符形式是为整数和浮点类型保留的。虽然123_s可以工作,但abc_s不能。如果我们完全抛弃用户定义的文字,只使用常规的constexpr函数会怎么样?

代码语言:javascript
复制
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */

和前面一样,我们遇到了这样的问题:数组现在是constexpr函数的参数,它本身不再是constexpr类型。

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

EN

回答 19

Stack Overflow用户

回答已采纳

发布于 2013-04-07 23:02:38

我还没有看到任何可以与C++ Now 2012上展示的Scott Schurr's str_const的优雅相媲美的东西。不过,它确实需要constexpr

下面是你如何使用它,以及它能做什么:

代码语言:javascript
复制
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的隐含版权。实现在他的演示文稿的一张幻灯片上链接到上面。

票数 132
EN

Stack Overflow用户

发布于 2013-04-10 05:24:01

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

使用非常简单的宏和一些C++11功能,可以在不依赖boost的情况下实现这一点:

通用常量expressions

  • non-static数据成员初始化器initialization

  1. uniform lambdas

(后两者在这里不是严格要求)

我们需要能够使用用户提供的从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;};}

  • 然后使用非类型参数char定义一个名为string的可变模板:

命名空间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{};\ }()

一个简单的拼接演示展示了其用法:

代码语言:javascript
复制
    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;
    }

https://ideone.com/8Ft2xu

票数 46
EN

Stack Overflow用户

发布于 2013-04-07 23:00:25

编辑:正如Howard Hinnant (和我在我对OP的评论中所指出的)所指出的,您可能不需要将字符串的每个字符都作为单个模板参数的类型。如果你确实需要这个,下面有一个无宏的解决方案。

我在编译时尝试处理字符串时发现了一个技巧。它需要引入“模板字符串”之外的另一种类型,但是在函数中,你可以限制这种类型的作用域。

它不使用宏,而是使用一些C++11特性。

代码语言:javascript
复制
#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();
}
票数 23
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15858141

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档