Open1
テンプレート引数に応じて文字リテラルの型を切り替える
5種類の文字列リテラルの型を切り替えられるようにしてみました。
template<class T>
constexpr std::basic_string_view<T> get_hello_world_string() {
return STRING_OF(T, "😎 Hello World");
}
STRING_OF
はマクロで、第2引数の文字列リテラルをコピーしてプレフィックスを結合し、型が異なる5種類の文字列リテラルからなる実引数列を生成します。そして、5個の引数から指定した型に合うものを1つ選んで返す関数に渡すことで、指定した型の文字の配列の参照が得られます。
#include <iostream>
#include <string_view>
#define STRING_OF(t, x) select_type<t>((x), (L ## x), (u8 ## x), (u ## x), (U ## x))
template<class T, size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t;
template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char, N1, N2, N3, N4, N5> {
using array_ref_t = const char (&)[N1];
static constexpr array_ref_t select(const char (&s)[N1], const wchar_t (&)[N2], const char8_t (&)[N3], const char16_t (&)[N4], const char32_t (&)[N5]) noexcept {
return s;
}
};
template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<wchar_t, N1, N2, N3, N4, N5> {
using array_ref_t = const wchar_t (&)[N2];
static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&s)[N2], const char8_t (&)[N3], const char16_t (&)[N4], const char32_t (&)[N5]) noexcept {
return s;
}
};
template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char8_t, N1, N2, N3, N4, N5> {
using array_ref_t = const char8_t (&)[N3];
static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&)[N2], const char8_t (&s)[N3], const char16_t (&)[N4], const char32_t (&)[N5]) noexcept {
return s;
}
};
template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char16_t, N1, N2, N3, N4, N5> {
using array_ref_t = const char16_t (&)[N4];
static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&)[N2], const char8_t (&)[N3], const char16_t (&s)[N4], const char32_t (&)[N5]) noexcept {
return s;
}
};
template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char32_t, N1, N2, N3, N4, N5> {
using array_ref_t = const char32_t (&)[N5];
static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&)[N2], const char8_t (&)[N3], const char16_t (&)[N4], const char32_t (&s)[N5]) noexcept {
return s;
}
};
template<class T, size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
[[nodiscard]] constexpr auto select_type(const char (&s1)[N1], const wchar_t (&s2)[N2], const char8_t (&s3)[N3], const char16_t (&s4)[N4], const char32_t (&s5)[N5]) noexcept -> typename select_type_t<T, N1, N2, N3, N4, N5>::array_ref_t
{
return select_type_t<T, N1, N2, N3, N4, N5>::select(s1, s2, s3, s4, s5);
}
template<class T>
constexpr std::basic_string_view<T> get_hello_world_string() {
return STRING_OF(T, "😎 Hello World");
}
int main()
{
std::cout << "sizeof" << std::endl;
std::cout << sizeof(STRING_OF(char, "😎 Hello World")) << std::endl;
std::cout << sizeof(STRING_OF(wchar_t, "😎 Hello World")) << std::endl;
std::cout << sizeof(STRING_OF(char8_t, "😎 Hello World")) << std::endl;
std::cout << sizeof(STRING_OF(char16_t, "😎 Hello World")) << std::endl;
std::cout << sizeof(STRING_OF(char32_t, "😎 Hello World")) << std::endl;
std::cout << "size()" << std::endl;
std::cout << get_hello_world_string<char>().size() << std::endl;
std::cout << get_hello_world_string<wchar_t>().size() << std::endl;
std::cout << get_hello_world_string<char8_t>().size() << std::endl;
std::cout << get_hello_world_string<char16_t>().size() << std::endl;
std::cout << get_hello_world_string<char32_t>().size() << std::endl;
return 0;
}
5種類もあるとコピペ感が強くなってきてよくないですね。可変引数テンプレートが使えないものか……。
似たような、というかほとんど同じテクニックがMS STLのテストコードで使われています。
MS STLとの違いは、文字列リテラルの長さを保存していることです。
C++20であれば、すべて consteval
にしてもよさそうです。