我是可变参数模板功能的新手。我编写了一个简单的类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):
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;
}

有什么办法可以解决这个问题?

最佳答案

错误的原因在于,字符串文字("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;
 }

09-25 18:28
查看更多