効率的な文字列連結

3つ以上の文字列を何気なく + 演算子で連結すると、必ずしも効率的には動作しないコードになります。

std::string a = "Hello";
const char b[] = "World";
char c = '!';
std::string x =
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    "<message>"
        + a + ',' + b + c
    + "</message>";

実際は、std::basic_string の実装が賢ければ、(たぶん)文字数が少ない場合にヒープを使わないとか、メモリを多めに確保して再確保の回数を減らすとかしてくれるし、コンパイラもムーブとか戻り値最適化とかしてくれるので、そういうのにおまかせで良い場合は + 演算子を使いまくれば良いと思います。

そうでない場合のために concat() という関数を書きました。

std::string x = concat(
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    "<message>",
        a, ',', b, c,
    "</message>");

引数に連結したい std::string や const char* や char を並べます。

実装は以下の通り。

#include <cstddef>
#include <cstring>
#include <string>

namespace detail {
struct ref
{
    const char* ptr;
    std::size_t size;
    ref(const char* s) : ptr(s), size(std::strlen(s)) {}
    ref(const std::string& s) : ptr(s.c_str()), size(s.size()) {}
    ref(const char& c) : ptr(&c), size(1) {}
};
} //namespace detail

template <typename... Args>
std::string concat(Args&& ...args)
{
    detail::ref refs[] = { detail::ref(args)... };
    std::size_t n = 0;
    for (detail::ref& r: refs)
    {
        n += r.size;
    }
    std::string s;
    s.reserve(n);
    for (detail::ref& r: refs)
    {
        s.append(r.ptr, r.size);
    }
    return s;
}

最初は Variadic Template の練習用に書き始めたのですが、結局何の変哲もないコードになりました。