#include <iostream>
#include <sstream>
#include <string>
#include <chrono>

template<typename TimeT = std::chrono::milliseconds>
struct measure {
    template<typename F, typename ...Args>
    static typename TimeT::rep execution(F&& func, Args&&... args) {
        auto start = std::chrono::system_clock::now();
        std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
        auto duration = std::chrono::duration_cast< TimeT>
            (std::chrono::system_clock::now() - start);
        return duration.count();
    }
};

std::string strAppend(const std::string &s, int cnt) {
    std::string str;
    for (int i = 0; i < cnt; ++i)
        str.append(s);
    return str;
}

std::string strOp(const std::string &s, int cnt) {
    std::string str;
    for (int i = 0; i < cnt; ++i)
        str += s;
    return str;
}

std::string strStream(const std::string &s, int cnt) {
    std::ostringstream oss;
    for (int i = 0; i < cnt; ++i)
        oss << s;
    return oss.str();
}

std::string strReserveAndOp(const std::string &s, int cnt) {
    std::string str;
    str.reserve(s.size() * cnt);
    for (int i = 0; i < cnt; ++i)
        str += s;
    return str;
}

std::string strReserveAndAppend(const std::string &s, int cnt) {
    std::string str;
    str.reserve(s.size() * cnt);
    for (int i = 0; i < cnt; ++i)
        str.append(s);
    return str;
}

int main()
{
    const std::string s("Hello world!");
    const int cnt = 1000000 * 5;
    std::cout << "ostringstream: " << measure<>::execution(strStream, s, cnt) << "ms" << std::endl;
    std::cout << "+= operator: " << measure<>::execution(strOp, s, cnt) << "ms" << std::endl;
    std::cout << "s.Append(): " << measure<>::execution(strAppend, s, cnt) << "ms" << std::endl;
    std::cout << "s.Reserve() & +=: " << measure<>::execution(strReserveAndOp, s, cnt) << "ms" << std::endl;
    std::cout << "s.Reserve() & s.Append(): " << measure<>::execution(strReserveAndAppend, s, cnt) << "ms" << std::endl;
}