Concatenating strings (and numbers) in variadic template function

inline std::string const& to_string(std::string const& s) { return s; }

template<typename... Args>
std::string stringer(Args const&... args)
{
    std::string result;
    using ::to_string;
    using std::to_string;
    int unpack[]{0, (result += to_string(args), 0)...};
    static_cast<void>(unpack);
    return result;
}

Why do not use simple std::stringstream ?

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

template< typename ... Args >
std::string stringer(Args const& ... args )
{
    std::ostringstream stream;
    using List= int[];
    (void)List{0, ( (void)(stream << args), 0 ) ... };

    return stream.str();
}

int main()
{
    auto s = stringer("hello", ' ', 23, ' ', 3.14, " Bye! " );

    std::cout << s << '\n';
}

Three more ways to do this:

  1. Similar to Khurshid's but without unnecessary array of ints
  2. Similar to Simple's and Khurshid's but builds on older compilers
  3. Recurent way

And the code:

#include <iostream>
#include <sstream>

// First version:
template<typename T>
std::string toString(T value)
{
    std::ostringstream oss;
    oss << value;
    return oss.str();
}

std::string merge(std::initializer_list<std::string> strList)
{
    std::string ret = "";
    for (std::string s : strList) {
        ret += s;
    }
    return ret;
}

template< typename ... Args >
std::string stringer1(const Args& ... args)
{
    return merge({toString(args)...});
}


// Second version:
template< typename ... Args >
std::string stringer2(const Args& ... args)
{
    std::ostringstream oss;
    int a[] = {0, ((void)(oss << args), 0) ... };

    return oss.str();
}


// Third version:
template<typename T>
std::string stringer3(const T& value)
{
    std::ostringstream oss;
    oss << value;
    return oss.str();
}

template<typename T, typename ... Args >
std::string stringer3(const T& value, const Args& ... args)
{
    return stringer3(value) + stringer3(args...);
}

int main()
{
    int a, b;
    std::cout << stringer1("a", 1) << std::endl;
    std::cout << stringer2("b", 2) << std::endl;
    std::cout << stringer3("c", 3) << std::endl;

// Output:
//     a1
//     b2
//     c3
}