Объединение строк (и чисел) в функции variadic template
Я пытаюсь написать функцию, которая принимает различные строки или числа (которые работают с std::to_string
и сцепить их. У меня он работает только со строками, но у меня проблемы со специализацией в зависимости от ввода как строки или числа.
мой код называется так:
stringer("hello", "world", 2, 15, 0.2014, "goodbye", "world")
и вот что у меня есть:
inline std::string stringer(const std::string &string)
{
return string;
}
template <typename T, typename... Args>
inline std::string stringer(const std::string &string, T &&val, Args &&...args)
{
return stringer(string+std::to_string(val), std::forward<Args>(args)...);
}
template <typename... Args>
inline std::string stringer(const std::string &string, Args &&...args)
{
return stringer(string, std::forward<Args>(args)...);
}
В настоящее время он разбивается на более чем одну строку, добавленную, если не все номера (из-за to_string). Как я могу специализироваться на строке или номере, чтобы сделать вышеуказанную работу? Спасибо.
5 ответов
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;
}
почему не использовать простой 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';
}
еще три способа сделать это:
- похож на Khurshid, но без ненужного массива ints
- похоже на Simple и Khurshid, но основывается на старых компиляторах
- Recurent способом
и код:
#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
}
на запрос, вот (более длинное) решение с SFINAE:
namespace detail {
using std::to_string;
std::string
concat()
{
return "";
}
template<typename Head, typename... Tail>
decltype( to_string(std::declval<Head>()) )
concat(Head&& h, Tail&&... t);
template<typename Head, typename... Tail>
decltype(std::string() + std::declval<Head>())
concat(Head&& h, Tail&&... t)
{
return std::forward<Head>(h) + concat(std::forward<Tail>(t)...);
}
template<typename Head, typename... Tail>
decltype( to_string(std::declval<Head>()) )
concat(Head&& h, Tail&&... t)
{
return to_string(std::forward<Head>(h)) + concat(std::forward<Tail>(t)...);
}
}
template<typename... Args>
std::string concat(Args&&... args)
{
return detail::concat(std::forward<Args>(args)...);
}
можно увидеть в действии здесь:http://coliru.stacked-crooked.com/a/77e27eaabc97b86b
обратите внимание, что для данного типа предполагается либо конкатенация строк (с +
) или to_string()
определено,но не оба. Так что std::string
, const char*
и любой сторонний класс строк, который естественным образом взаимодействует с std::string
должен пройти через +
версия. Конечно, если 3rd сторона класс string делает что-то глупое, как определение конкатенации и to_string()
это будет неоднозначно; вам нужно будет определить has_string_concat
и has_to_string
введите предикаты, чтобы иметь контроль над тем, как разрешить двусмысленность.
я также помещаю все в пространство имен, чтобы иметь возможность использовать зависящий от аргумента поиск для выбора правильной версии to_string
; полный пример показывает пользовательский тип с собственным to_string()
.
В C++ 11, делать это в variadic templates way работает для меня. Он также принуждает пользователя предоставить хотя бы один аргумент. inline
полезно, если вы определяете функции в заголовочном файле.
#include <sstream>
template< typename T >
inline std::string stringify(const T& t)
{
std::stringstream string_stream;
string_stream << t;
return string_stream.str();
}
template< typename T, typename ... Args >
inline std::string stringify(const T& first, Args ... args)
{
return stringify( first ) + stringify( args... );
}