C++ で文字列連結を行う方法はいくつかあるが,単一式で書けなかったり,STL コンテナを使ったりなど,どれも気軽には使いにくい感がある. 今回は可変長引数を使って,使い勝手のよい文字列連結関数を実装した.
背景
次のコードは,文字列連結についての2つの問題を,C++ の標準的な方法で実装したものである.
//! Example 1: "usr", "bin", "gcc" -> "/usr/bin/gcc" // + return "/" + "usr" + "/" + "bin" + "/" + "gcc"; // stringstream stringstream ss; ss << "/" << "usr" << "/" << "bin" << "/" << "gcc"; return ss.str(); // boost::algorithm::join std::vector<std::string> v = {"", "usr", "bin", "gcc"}; return boost::algorithm::join(v, "/"); //! Example 2: "image", 2014, "summer.png" -> "image-2014-summer.png" // + return "image" + "-" + 2014 + "-" + "gcc"; // Error! return "image" + "-" + itoa(2014) + "-" + "gcc"; // Not standard // stringstream stringstream ss; ss << "image" << "-" << 2014 << "-" << "summer.png"; return ss.str(); // boost::algorithm::join ? v = {"image", 2014, "summer.png"}; // Which type to use? return boost::algorithm::join(v, "-");
C++ で文字列連結をしようと思うと上の3通りが標準的だと思うが,どれも短所がある.
+
を使う方法は型の制限がきつく,boost::algorithm::join
は一度コンテナを作らなければいけない.
唯一2問目を解ける stringstream
でも,単一式として書くことができず,最低でも3文を書かなければならないという欠点がある.
というわけで今回の目標は,混合型で使えて,かつ式として書ける文字列連結関数 Join を実装することである.インターフェースは次のようになる.
return Join("/", "", "usr", "bin", "gcc"); return Join("-", "image", 2014, "summer.png");
本当は Join("image", 2014, "summer.png", sep="-")
と書きたいところだが,C++ の制約上可変長引数と名前付き引数が両立しないので,上の形で妥協することにする.
実装
#include <sstream> #include <string> template<typename T, typename U, typename... R> void Join(std::stringstream& ss, const T& sep, const U& head, const R&... follows) { ss << head << sep; Join(ss, sep, follows...); } template<typename T, typename U> void Join(std::stringstream& ss, const T& sep, const U& head) { ss << head; } template<typename T, typename U, typename... R> std::string Join(const T& sep, const U& head, const R&... follows) { std::stringstream ss; Join(ss, sep, head, follows...); return ss.str(); }
英文字がごみごみして読みにくい