Функция шаблона Variadic для создания строки
Я новичок в вариативных шаблонных функциях. Я написал простой класс,StringStream
, который имеет вариативную функцию шаблона, которая создает std::string
из переменных аргументов шаблона - строк, целых чисел и т. д.
#include <string>
#include <sstream>
class StringStream
{
public:
StringStream() = default;
~StringStream() = default;
template<typename T>
std::string Stringify(const T &value)
{
mStream << value;
return mStream.str();
}
template<typename T, typename... Ts>
std::string Stringify(const T& value, Ts... values)
{
mStream << value;
return Stringify(values...);
}
private:
std::stringstream mStream;
};
Сейчас я хочу использовать std::string
член в StringStream
вместо того std::stringstream
и построить строку из аргументов Stringify()
. Для аргументов, которые неstd::string
Я хочу преобразовать в строки с помощью std::to_string()
, иначе я просто объединяю аргумент. Я столкнулся с ошибкой компилятора. Вот мой модифицированный класс:
class StringStream
{
public:
StringStream() = default;
~StringStream() = default;
template<typename T>
std::string Stringify(const T &value)
{
mString += std::to_string(value);
return mString;
}
template<>
std::string Stringify<std::string>(const std::string& value)
{
mString += value;
}
template<typename... Ts>
std::string Stringify(const std::string& value, Ts... values)
{
mString += value;
return Stringify(values...);
}
template<typename T, typename... Ts>
std::string Stringify(const T& value, Ts... values)
{
mString += std::to_string(value);
return Stringify(values...);
}
private:
std::string mString;
};
Моя ошибка компилятора говорит:
ошибка C2665: 'std::to_string': ни одна из 9 перегрузок не смогла преобразовать все типы аргументов
Я вызываю функцию так:
int main()
{
int age;
std::cin >> age;
StringStream ss;
std::cout << ss.Stringify("I", " am ", age, " years ", "old") << std::endl;
}
Есть ли способ решить эту проблему?
2 ответа
Причина ошибки в том, что строковые литералы ("I"
, " am "
, " years "
, "old"
) - массивы постоянных char
s (char const [N]
, для некоторых N
). Вы можете перехватить их какchar const *
но не как std::string
.
Я полагаю, это немного не по теме, но я дам вам два предложения:
(1) разделить Stringify()
в двух функциях: вариативной, public
, которые называют private
один (toStr()
в моем следующем примере), чтобы преобразовать одиночные аргументы
(2) избегать рекурсии для вариативной версии Stringify()
но просто используйте расширение пакета.
Я имею ввиду... ты можешь писать Stringify()
следующее
template <typename... Ts>
std::string Stringify (Ts const & ... vals)
{
using unused = int[];
(void)unused { 0, (mString += toStr(vals), 0)... };
return mString;
}
или, если вы можете использовать C++17, используя сворачивание шаблона
template <typename... Ts>
std::string Stringify (Ts const & ... vals)
{ return ((mString += toStr(vals)), ...); }
За toStr()
, Я предлагаю версию шаблона, в которой используется std::to_string()
но включается только тогда, когда шаблон T
тип не конвертируется в std::string
template <typename T>
typename std::enable_if<
false == std::is_convertible<T, std::string>::value,
std::string>::type toStr (T const & val)
{ return std::to_string(val); }
и версия без шаблона, которая принимает std::string
std::string toStr (std::string const & val)
{ return val; }
Таким образом, если аргумент можно напрямую преобразовать в std::string
(является std::string
или другой тип, который можно использовать для построения std::string
) вызывается нешаблонная версия; иначе называется шаблонным.
Ниже приведен полный пример компиляции.
#include <iostream>
#include <type_traits>
class StringStream
{
private:
std::string mString;
template <typename T>
typename std::enable_if<
false == std::is_convertible<T, std::string>::value,
std::string>::type toStr (T const & val)
{ return std::to_string(val); }
std::string toStr (std::string const & val)
{ return val; }
public:
StringStream() = default;
~StringStream() = default;
template <typename... Ts>
std::string Stringify (Ts const & ... vals)
{
using unused = int[];
(void)unused { 0, (mString += toStr(vals), 0)... };
return mString;
}
};
int main ()
{
int age = 42;
StringStream ss;
std::cout << ss.Stringify("I", " am ", age, " years ", "old") << std::endl;
}
Вы звоните to_string
внутри
template<typename T>
std::string Stringify(const T &value)
{
mString += std::to_string(value);
return mString;
}
так что вы можете удалить to_string
от Stringify(const T& value, Ts... values)
и замените его просто Stringify
:
template<typename T, typename... Ts>
std::string Stringify(const T& value, Ts... values)
{
mString += Stringify(value);
return Stringify(values...);
}
добавьте также специализацию для const char*:
std::string Stringify(const char* value)
{
return mString += value;
}
Теперь ваша проблема в том, что const char[2]
передается to_string
, но to_string
не имеет перегрузки, которая принимает этот ввод. Заменивto_string
по Stringify
соответствующая перегрузка может использоваться для первого аргумента из пакета аргументов.