c++ - 用于创建字符串的 Variadic 模板函数

标签 c++ c++11 variadic-templates variadic-functions template-argument-deduction

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

我现在想做的是在 StringStream 中使用 std::string 成员而不是 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;
};

我的编译器错误是:

error 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 数组(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;
 }

关于c++ - 用于创建字符串的 Variadic 模板函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57831060/

相关文章:

c++ - 错误 : no instance of overloaded function "std::make_shared" matches the argument list

linux - 哪个工具或命令可以提供非常准确的 Linux 内存使用状态?

c++ - C++ 标准的哪一部分阻止显式指定此模板的参数?

c++ - 将类型对列表的 vector 大小设置为用户在类中给定的大小

android - 非游戏果酱应用程序的好例子?

c++ - Windows 线程(c 运行时、pthreads、std::thread)

c++ - 可变参数模板模板参数语法

c++ - 用iibjson试试,catch不行,在target Cross compiled linux board

c++ - 理性类和 move 语义不起作用

c++ - 使用参数包和元组创建一个简单的表达式类