Функция шаблона 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") - массивы постоянных chars (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 соответствующая перегрузка может использоваться для первого аргумента из пакета аргументов.

Другие вопросы по тегам