如何将函数转换为模板函数

C++ - How to convert a function to a template function

本文关键字:函数 转换      更新时间:2023-10-16

我有一个C++函数,它接受逗号分隔的字符串并在std::vector<std::string>中分割:

std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty = true) {
    std::vector<std::string> result;
    if (delim.empty()) {
        result.push_back(s);
        return result;
    }
    std::string::const_iterator substart = s.begin(), subend;
    while (true) {
        subend = std::search(substart, s.end(), delim.begin(), delim.end());
        std::string temp(substart, subend);
        if (keep_empty || !temp.empty()) {
            result.push_back(temp);
        }
        if (subend == s.end()) {
            break;
        }
        substart = subend + delim.size();
    }
    return result;
}

然而,我真的希望能够将此函数应用于多种数据类型。例如,如果我输入std::string:

1,2,3,4,5,6

那么我希望函数的输出是int s的向量。我对C++相当陌生,但我知道有一种叫做template类型的东西,对吧?是否可以将此函数创建为通用模板?还是我误解了template功能的工作方式?

可以将模板函数声明为:

template<class ReturnType>
std::vector<ReturnType> split(const std::string&, const std::string&, const bool = true);

,然后对你想要允许的每种向量类型进行专门化:

template<>
std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty) {
    // normal string vector implementation
}
template<>
std::vector<int> split(const std::string& s, const std::string& delim, const bool keep_empty) {
    // code for converting string to int
}
// ...

你可以在这里阅读关于string到int的转换。

你将需要调用split作为:

auto vec = split<int>("1,2,3,4", ",");

你可以"模板化"这个函数-启动它,你只需要在函数之前用'std::vector and add template '替换std::vector<std::string>。但是你需要注意如何将字符串放入结果向量中。在当前的实现中,只有

result.push_back(temp);

因为result是string类型的vector,而temp是string类型的。在一般情况下,这是不可能的,如果你想使用这个函数,例如vector<int>,这一行将无法编译。然而,这个问题很容易用另一个函数来解决——还是模板——它将字符串转换成你想要使用split的任何类型。我们把这个函数命名为convert:

template<typename T> T convert(const std::string& s);

那么你需要为你需要的任何类型提供这个函数的专门化。例如:

template<> std::string convert(const std::string& s) { return s; }
template<> int convert(const std::string& s) { return std::stoi(s); } 

通过这种方式,您不需要像另一个答案所建议的那样专门化整个函数,只需根据类型专门化部分即可。对于

行也应执行相同的操作。
result.push_back(s);

您的函数可以很容易地一般化,使用Boost.LexicalCast返回任意类型的vector。唯一的问题是:

if (delim.empty()) {
    result.push_back(s);
    return result;
}

这只适用于现在,因为输入和输出类型都是std::string,但显然不能工作,如果你返回的vector包含std::string以外的类型。使用boost::lexical_cast执行这样的无效转换将导致抛出boost::bad_lexical_cast。所以也许你想重新考虑这部分,但除此之外,实现是直截了当的。

#include <boost/lexical_cast.hpp>
template<typename Result>
std::vector<Result> 
    split(const std::string& s, const std::string& delim, const bool keep_empty = true) 
{
    std::vector<Result> result;
    if (delim.empty()) {
        result.push_back(boost::lexical_cast<Result>(s));
        return result;
    }
    std::string::const_iterator substart = s.begin(), subend;
    while (true) {
        subend = std::search(substart, s.end(), delim.begin(), delim.end());
        std::string temp(substart, subend);
        if (keep_empty || !temp.empty()) {
            result.push_back(boost::lexical_cast<Result>(temp));
        }
        if (subend == s.end()) {
            break;
        }
        substart = subend + delim.size();
    }
    return result;
}

基本上,我所做的就是使结果类型成为模板参数,并替换了
result.push_back(x);

result.push_back(boost::lexical_cast<Result>(x));

如果您不能使用Boost,请看看这个答案,它展示了如何使用stringstream将字符串转换为其他类型。