C ++ - 如何将函数转换为模板函数

时间:2014-07-14 21:51:40

标签: c++ c++11

我有一个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函数的工作原理?

3 个答案:

答案 0 :(得分:4)

您可以将模板函数声明为:

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
}

// ...

您可以阅读有关字符串到整数转换的here

然后,您需要将split称为:

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

答案 1 :(得分:4)

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

result.push_back(temp);

因为result是字符串的向量,而temp是字符串。在一般情况下虽然不可能,并且如果你想使用这个功能,例如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);

在没有分隔符的情况下。

答案 2 :(得分:3)

使用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,请查看this answer,了解如何使用stringstream将字符串转换为其他类型。