質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.44%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

767閲覧

char/wchat_tの文字列リテラルをテンプレートを使って綺麗に共通化したい

dameo

総合スコア964

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2023/03/30 06:42

編集2023/03/30 06:52

質問

同じ文字列を表現するchar/wchat_tの文字列リテラルをテンプレートを使って綺麗に共通化したい(正規化ではないです)。

前提

  • プラットフォーム/処理系はできるだけ問わない
  • C++11以降、もしくはC++14以降で動作すること(C++17以降の固有機能は使用しない)

背景

以下の例を参照

マルチバイト文字列(std::string)とワイド文字列(std::wstring)をそれぞれ日本語の括弧「」で括って表示するプログラムです。

C++

1#include <iostream> 2#include <locale> 3#include <string> 4#include <codecvt> 5using namespace std; 6string japanese_quote(const string& s) {return "「" + s + "」";} 7wstring japanese_quote(const wstring& s) {return L"「" + s + L"」";} 8int main() { 9 setlocale(LC_ALL, ""); 10 string mbs = japanese_quote(string("ほげ")); 11 wstring ws = japanese_quote(wstring(L"ほげ")); 12 cout << mbs << endl; 13 wstring_convert<codecvt_utf8<wchar_t>, wchar_t> conv; // deprecated by c++17 14 cout << conv.to_bytes(ws) << endl; 15 return 0; 16} 17// 出力: 18// 「ほげ」 19// 「ほげ」

ココでオーバーロードされているjapanese_quoteをテンプレートで共通化したいのですが、文字列リテラルをどうやって切り替えるかに悩んでいます。例えば、以下はエラーになります。

C++

1#include <iostream> 2#include <locale> 3#include <string> 4#include <codecvt> 5using namespace std; 6template<typename Char> 7basic_string<Char> japanese_quote(const basic_string<Char>& s) { 8 return "「" + s + "」"; 9} 10int main() { 11 setlocale(LC_ALL, ""); 12 string mbs = japanese_quote(string("ほげ")); 13 wstring ws = japanese_quote(wstring(L"ほげ")); 14 cout << mbs << endl; 15 wstring_convert<codecvt_utf8<wchar_t>, wchar_t> conv; // deprecated by c++17 16 cout << conv.to_bytes(ws) << endl; 17 return 0; 18}

std::wstringのとき、つまりCharがwchar_tのときに"「"や"」"などのリテラルをstd::wstringにできないからです(ワイド文字列リテラルでないため)。ワイド文字列リテラルにするにはL"「"やL"」"などとしないといけません。これをうまく実装する方法を尋ねるべく質問した次第です。

試したこと

背景の例でですが、今のところ何とか実装はできています。しかし綺麗とは言い難いし、まだ何とかなるような気がするので、お知恵をお借りしてもう少し綺麗にしたいのです。現状のコードは以下のとおりです。

C++

1#include <iostream> 2#include <locale> 3#include <string> 4#include <codecvt> 5using namespace std; 6#define LITERAL_CLASS(NAME, VALUE) \ 7template<typename T> \ 8struct NAME { \ 9 static const T* value(); \ 10}; \ 11template<> const char* NAME<char>::value() {return VALUE;} \ 12template<> const wchar_t* NAME<wchar_t>::value() {return L##VALUE;} 13LITERAL_CLASS(jp_bra, "「") 14LITERAL_CLASS(jp_ket, "」") 15template<typename Char> 16basic_string<Char> japanese_quote_tmpl(const basic_string<Char>& s) { 17 return jp_bra<Char>::value() + s + jp_ket<Char>::value(); 18} 19int main() { 20 setlocale(LC_ALL, ""); 21 string mbs = japanese_quote_tmpl(string("ほげ")); 22 wstring ws = japanese_quote_tmpl(wstring(L"ほげ")); 23 cout << mbs << endl; 24 wstring_convert<codecvt_utf8<wchar_t>, wchar_t> conv; // deprecated by c++17 25 cout << conv.to_bytes(ws) << endl; 26 return 0; 27}

汚いマクロを使ってる上に、グローバルが汚れています。もう少し上手くできないものなのでしょうか?どこかでこういうコード見たよ!などの情報だけでも結構です。よろしくお願いします。

注意点

リテラルでないといけません。実行時にstringからwstringにするのは簡単ですが、それなりにコストがかかります。今は例なのでこれだけですが、数はいくらでも増えるし、パフォーマンスを気にする部分と思ってください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「リテラルとして」というのを前提とすると char/wchar_t を切り替えるのは接頭辞 L しかありません。 そしてそれをどうにかして抽象化した形にまとめる言語機能は C++ にはマクロしか選択肢がありません。

私なりに考えると最終的にこういう形になりました。

cpp

1#include <string> 2#include <tuple> 3 4#define polystring(str, type) std::get<const type *>(std::make_tuple(str, L##str)) 5 6template <class T> 7auto enclose(const std::basic_string<T> &str) -> std::basic_string<T> { 8 return polystring("「", T) + str + polystring("」", T); 9}

一旦は charwchar_t のリテラルを作ってから選択をしていますが gcc や clang で最適化をかけて試してみたら getmake_tuple も消滅してリテラルだけが残るようなので実行コスト的にも無駄はないはずです。

投稿2023/03/30 16:28

SaitoAtsushi

総合スコア5515

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dameo

2023/03/30 16:33

これしかないという回答来ましたね。なるほどクラスにするまでもなかったですか。 std::getで型指定は思いつかなかったですね。ありがとうございます。スッキリしました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.44%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問