在“char 类型”模板化类中使用字符串文字

Posted

技术标签:

【中文标题】在“char 类型”模板化类中使用字符串文字【英文标题】:Use string litterals in a "char type" templated class 【发布时间】:2015-12-26 17:18:05 【问题描述】:

我在 C++ 中有一个模板类,它将char_type 模板参数作为字符类型,例如charwchar_tchar32_t 等... 然后该类在代码。

然后在课堂上的某个地方,我填写了一个转义序列表,例如"&"。这不起作用,因为取决于模板字符类型,我们需要使用"&"L"&"U"&"...

有没有办法避免专门用于初始化表的模板函数,例如使用一些标准函数来转换字符串?

由于这些是转义序列,它们除了 ASCII 字符外不包含任何其他内容。

【问题讨论】:

所以你本质上需要一个多态字符串文字?嗯…… 不,我想在编译时这样做 C++ 也有编译时多态性,但我不确定这里是否可行。 @templates 不。模板是一种允许多态性的机制,但我离题了。让我考虑一下可能的解决方案。 不知道为什么会有反对票。这是一个很好的问题,而且(令人惊讶的是)我找不到骗子。 【参考方案1】:

我会做以下事情:

template <typename char_type, size_t LENGTH>
constexpr std::basic_string<char_type> literal(const char (&value)[LENGTH])

    using string = std::basic_string<char_type>;

    string result;
    result.reserve(LENGTH);

    std::copy(std::begin(value), std::end(value), std::back_inserter(result));

    return result; // rvo

你可以这样使用它:

// Table of escaping sequences
std::basic_string<char_type> escaping_sequences[] =

    literal<char_type>("&amp"),
    literal<char_type>("&foo"),
    literal<char_type>("&bar"),
    ...

我已经测试过了in Ideone:

literal<  char  >("test") // result: std::string
literal<char32_t>("test") // result: std::basic_string<char32_t, std::char_traits<char32_t>, std::allocator<char32_t> >
literal<char16_t>("test") // result: std::basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t> >

未针对所有 char 类型进行测试,但希望对您有所帮助。

编辑 1

我的错,我刚刚注意到 galinette 几乎和我之前回答的一样。我的代码和 galinette 的代码之间的唯一区别是,我 使用 reserve 分配结果字符串一次,而不是使用 push_back 的自动分配 在编译时计算字符数, 由于使用了LENGTH 作为模板参数。

编辑 2

可以通过在end 迭代器中减去 1 来避免最终的空字符问题:

template <typename char_type, size_t LENGTH>
constexpr std::basic_string<char_type> literal(const char (&value)[LENGTH])

    using string = std::basic_string<char_type>;

    string result;
    result.reserve(LENGTH - 1);

    std::copy(std::begin(value), std::end(value) - 1, std::back_inserter(result));

    return result; // rvo

或者,使用std::copy_n 代替std::copy

template <typename char_type, size_t LENGTH>
constexpr std::basic_string<char_type> literal(const char (&value)[LENGTH])

    using string = std::basic_string<char_type>;

    string result;
    result.reserve(LENGTH - 1);

    std::copy_n(std::begin(value), LENGTH - 1, std::back_inserter(result));

    return result; // rvo

【讨论】:

我也用过reserve!但是您的解决方案更好,因为它不计算运行时的字符数,因为使用 LENGTH 作为模板参数是个好主意。 @galinette 我今天过得多么开心......你说得对,你也在使用reserve,但我这一天充满了误读:'( 非常感谢这个优雅的解决方案!使用此代码后只是一个小评论:我遇到了上述代码还在结果中包含最终空字符的问题(即尝试评估literal(“test”).length(),这会给出5 4)的预期。我不确定解决这个问题的最佳方法是什么。 @galinette 的解决方案没有遇到这个问题,因为在该代码中,变量 s 设置为不包括空字符的长度。 @MatthiasC.M.Troffaes 应该很容易解决这个空字符问题,保留LENGTH - 1 字符并使用std::copy_n 而不是std::copy(我会编辑答案)。跨度> MSVC 2015,它显然具有“一些”c++14 功能,但没有“扩展 constexpr”。也许这就是原因。 msdn.microsoft.com/en-us/library/hh567368.aspx【参考方案2】:

最好的方法可能是自己定义转换函数,因为将 ASCII 转换为 UTF8/16/32 是对 char 类型的直接转换

template<typename char_type>
std::basic_string<char_type> cvtASCIItoUTFX(const char * litteral)

    //We could define a faster specialization in case char_type is char

    size_t s = strlen(litteral);

    std::basic_string<char_type> result;
    result.reserve(s);
    for(size_t i=0;i<s;++i)
    
        result.push_back((char_type)litteral[i]);
    

    return result;

【讨论】:

【参考方案3】:

由于这些是转义序列,它们除了 ASCII 字符之外不包含任何其他内容。

有没有办法避免专门用于初始化表的模板函数,例如使用一些标准函数来转换字符串?

不,因为该标准没有任何转换函数可以坚持这些特定的子集。

我建议只为表格使用外部生成器,或者如果您真的想留在 C++ 中,请使用宏。

【讨论】:

由于 ASCII 字符串是有效的 UTF8 字符串,并且由于 c++11 具有 utf8 到 utf16 的转换,所以这并不完全正确。 如果你正确地阅读了我的主张,你会发现使用超集来反驳它是没有意义的。但除此之外,标准中没有足够的转换函数。您必须手动完成。【参考方案4】:

此答案仅适用于非字符串(即数字)文字

...因为只有那些被语言扩展为template&lt;char...&gt;

因为我在这上面花了一段时间,我想我不妨把它贴在这里。不适用于实际的字符文字,因为 herp derp C++.

template<char16_t... str>
struct Literal16 
    static constexpr char16_t arr[] = str...;
    
    constexpr operator const char16_t*()  
        return arr;
    
;

template<char... str>
struct Literal8 
    static constexpr char arr[] = str...;
    
    constexpr operator const char*()  
        return arr;
    
;

template<char... str>
struct PolyLiteral 
    operator const char*() 
        return Literal8<str...>();
    
    operator const char16_t*() 
        return Literal16<str...>();
      
;

template<char... str> PolyLiteral<str...> operator"" _poly()  return PolyLiteral<str...>(); 

int main() 
    const char* test = 123_poly;
    const char16_t* test2 = 123_poly;

【讨论】:

Doh,这是一个很好的操作符黑客...不过,这可能会使代码难以阅读。【参考方案5】:

如果您可以使用宏:

#define TEXT_char(text) (text)
#define TEXT_wchar_t(text) (L ## text)
//...etc
#define TEXT(type, text) (TEXT_##type(text))

int main() 

    const char* c = TEXT(char, "Test");
    const wchar_t* w = TEXT(wchar_t, "Test");

【讨论】:

这没有回答问题。

以上是关于在“char 类型”模板化类中使用字符串文字的主要内容,如果未能解决你的问题,请参考以下文章

C#关键字详解第四节

JAVA数据类型中的char类型

将类的任何模板存储在另一个类中

数据类型

在实例化类之前在 Python 类中定义的方法的正确术语是啥?

使用字符串文字作为 Django 模板中模板标签的参数