为不同字符串类别适配字符串文本字面量

5

问题

我正在实现一个类,想让用户通过模板参数选择字符串类型 (std::string, std::wstring, std::u16string, ...)。但是我目前无法使字符串字面值与所选的字符串类型匹配:一旦我决定使用字面值前缀 ("hello" vs. L"hello" vs. u"hello" vs. U"hello"),所有不兼容的字符串类都会出现编译错误。

示例代码

请看以下示例代码(使用--std=c++11编译):

#include <string>

template<typename StringType>
void hello_string()
{
    StringType result("hello");
}

int main()
{
    // works
    hello_string<std::string>();
    hello_string<std::basic_string<char>>();

    // the code below does not compile
    hello_string<std::wstring>();
    hello_string<std::basic_string<unsigned char>>();
    hello_string<std::u16string>();
}

hello_string()函数展示了我想要做的核心:将字符串类型作为模板参数,然后将字符串字面量赋值给此类型的变量。

可能的解决方法

克服我的问题的一种方法是实现hello_string()函数的几个特化版本。问题在于这会导致每个字符串字面量的多个副本——每个字符串字面量前缀一个。我认为这相当丑陋,并且必须有更好的方法。

另一种方法可能是选择“普通”的字符串字面量作为默认值,并使函数进行不同字符串类型的转换。虽然这可以避免代码重复,但它会引入对实际上是常数的东西进行不必要的转换。


const char kHello[] = "hello"; StringType result(kHello, kHello + sizeof(kHello) - 1); 只适用于纯ASCII。 - Igor Tandetnik
谢谢,我忘记了这个构造函数类型对于所有basic_strings都是通用的。 - Niels Lohmann
1个回答

2
您可以自己创建一个宏。首先定义一个结构体来包装字符选择:
namespace details {

    template<typename T>
    struct templ_text;

    template<>
    struct templ_text <char>
    {
        typedef char char_type;
        static const char_type * choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return narrow; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return narrow; }
    };

    template<>
    struct templ_text < wchar_t >
    {
        typedef wchar_t char_type;
        static const char_type* choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return wide; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return wide; }
    };

    template<>
    struct templ_text < char16_t >
    {
        typedef char16_t char_type;
        static const char_type* choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return u16; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return u16; }
    };

    template<>
    struct templ_text < char32_t >
    {
        typedef char32_t char_type;
        static const char_type* choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return u32; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return u32; }
    };
}

将其封装成一个好的宏:
#define TEMPL_TEXT(Ch, txt) details::templ_text<Ch>::choose(txt, L##txt, u##txt, U##txt)

那么您的函数将是:
template<typename StringType>
void hello_string()
{
    StringType result(TEMPL_TEXT(typename StringType::value_type, "Hello"));
}

我认为未使用的字符串副本将被优化掉。

谢谢你的回答!我知道可以使用宏添加文字前缀,但我找不到将其与模板结合的方法。 - Niels Lohmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接