可变参数模板函数创建字符串

时间:2019-09-07 05:50:05

标签: 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;
};

我现在想要做的是在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 个答案:

答案 0 :(得分:2)

错误的原因是,字符串文字("I"" am "" years ""old")是常数char s({ {1}}(对于某些char const [N])。您可以将其拦截为N,而不能拦截为char const *

我想这有点偏离主题,但是我给你两个建议:

(1)将std::string划分为两个函数:可变参数Stringify(),该函数调用public一个(在下面的示例中为private)进行转换超过单打参数

(2)避免对toStr()的可变版本进行递归,而只需使用包扩展。

我的意思是...您可以按如下方式写Stringify()

Stringify()

或者,如果可以使用C ++ 17,请使用模板折叠

  template <typename... Ts>
  std::string Stringify (Ts const & ... vals)
   {
     using unused = int[];

     (void)unused { 0, (mString += toStr(vals), 0)... };

     return mString;
   }

对于 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

答案 1 :(得分:1)

您正在内部致电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;
}

Live demo

现在您的问题是const char[2]传递了to_string,但是to_string没有接受该输入的重载。通过将to_string替换为Stringify,可以将适当的重载用于参数包中的第一个参数。