【问题标题】:Trouble passing a template function as an argument to another function in C++将模板函数作为参数传递给 C++ 中的另一个函数时遇到问题
【发布时间】:2010-04-23 16:35:55
【问题描述】:

问题根源-加速C++,问题8-5

我编写了一个小程序,它检查字符串输入的行,并计算一个单词在给定行上出现的次数。下面的代码实现了这一点:

#include <map>
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <cctype>
#include <iterator>

using std::vector;         using std::string;
using std::cin;            using std::cout;
using std::endl;           using std::getline;
using std::istream;        using std::string;
using std::list;           using std::map;
using std::isspace;        using std::ostream_iterator;
using std::allocator;

inline void keep_window_open()
{
    cin.clear();
    cout << "Please enter EOF to exit\n";
    char ch;
    cin >> ch;
    return;
}

template <class Out>
void split(const string& s, Out os)
{
    vector<string> ret;
    typedef string::size_type string_size;
    string_size i = 0;

    // invariant: we have processed characters `['original value of `i', `i)'
    while (i != s.size()) {
        // ignore leading blanks
        // invariant: characters in range `['original `i', current `i)' are all spaces
        while (i != s.size() && isspace(s[i]))
            ++i;

        // find end of next word
        string_size j = i;
        // invariant: none of the characters in range `['original `j', current `j)' is a space
        while (j != s.size() && !isspace(s[j]))
            ++j;

        // if we found some nonwhitespace characters
        if (i != j) {
            // copy from `s' starting at `i' and taking `j' `\-' `i' chars
            *os++ = (s.substr(i, j - i));
            i = j;
        }
    }
}


// find all the lines that refer to each word in the input
map<string, vector<int> > xref(istream& in)     // works
// now try to pass the template function as an argument to function - what do i put for templated type?
//map<string, vector<int> > xref(istream& in, void find_words(vector<string, typedef Out) = split)      #LINE 1#
{
    string line;
    int line_number = 0;
    map<string, vector<int> > ret;

    // read the next line
    while (getline(in, line)) {
        ++line_number;

        // break the input line into words
        vector<string> words;   // works            // #LINE 2#
        split(line, back_inserter(words));          // #LINE 3#
        //find_words(line, back_inserter(words));   // #LINE 4# attempting to use find_words as an argument to function

        // remember that each word occurs on the current line
        for (vector<string>::const_iterator it = words.begin();
             it != words.end(); ++it)
            ret[*it].push_back(line_number);
    }
    return ret;
}

int main()
{
    cout << endl << "Enter lines of text, followed by EOF (^Z):" << endl;

    // call `xref' using `split' by default
    map<string, vector<int> > ret = xref(cin);

    // write the results
    for (map<string, vector<int> >::const_iterator it = ret.begin();
         it != ret.end(); ++it) {
        // write the word
        cout << it->first << " occurs on line(s): ";

        // followed by one or more line numbers
        vector<int>::const_iterator line_it = it->second.begin();
        cout << *line_it;   // write the first line number

        ++line_it;
        // write the rest of the line numbers, if any
        while (line_it != it->second.end()) {
            cout << ", " << *line_it;
            ++line_it;
        }
        // write a new line to separate each word from the next
        cout << endl;
    }
    keep_window_open();
    return 0;
}

如您所见,split函数是一个模板函数,可以根据需要处理各种类型的输出迭代器。

当我尝试通过传入模板化的 split 函数作为参数来概括 xref 函数时,我的问题就出现了。我似乎无法正确输入类型。

所以我的问题是,你可以将模板函数作为参数传递给另一个函数吗?如果可以,是否必须在传递之前声明所有类型?或者编译器可以根据模板化函数在主体中的使用方式来推断类型吗?

为了演示我得到的错误,注释掉现有的 xref 函数标题,并取消注释我正在尝试工作的备用标题(就在下面的注释行下方。)同时注释这些行标记了第 2 行和第 3 行并取消注释第 4 行,它正在尝试使用参数 find_words(默认为 split。)

感谢您的任何反馈!

【问题讨论】:

  • 请尝试发布 minimal 示例代码。
  • 此外,C++ 中没有“模板函数”之类的东西。这是一个“功能模板”。
  • 我认为一个简单的using namespace std; 就足够了:-)
  • 并发布不起作用的代码 - 不要让我们自己编辑它。我打算将您的代码编辑为基本问题,但我真的不知道您的问题是什么,或者如何转换您的代码以说明问题。
  • 积分。我认为发布一个完整的、有效的程序将最好地展示我正在尝试做的事情。

标签: c++ templates stl


【解决方案1】:

以下是一种解决方法。 您可以使用带有静态函数的类

struct split
{
  template <class Out>
  static apply(const string& s, Out os) { 
    // include the body of your split function or call to an existing function
  }

};

现在,使外部参照通用

template <typename FIND_WORDS>
map<string, vector<int> > xref(istream& in, FIND_WORDS find_words = split()) 
{

  // replace #2 and #3 by
  find_words.apply(line, back_inserter(words));

}; 

【讨论】:

  • 感谢您的回复。当我进行这些更改时,我仍然收到错误“无法推断'FIND_WORDS'的模板参数”。我会继续努力,看看能不能让它发挥作用。
  • 在必要时添加 tympename 或将 find_words::apply 替换为 find_words.apply
  • 我能够让它工作,但不能使用默认参数参数。我必须创建一个拆分“s”,然后调用 xref(cin, s)。这允许外部参照调用静态函数 s.apply()。这是使用外部参照(istream& in,FIND_WORDS s)。我尝试将 xref(cin) 与 xref(istream& in, FIND_WORDS s = split) 一起使用,但它无法编译 - 表示没有外部参照实例与参数列表匹配。
【解决方案2】:

或者编译器可以根据模板化函数在主体中的使用方式来推断类型吗?

这个问题的答案是:不。

您需要将 'typedef Out' 更改为 back_inserter 返回的类型,并且需要将相同的类型提供给“= split”。

back_inserter 返回的类型由标准指定,因此您应该能够在您的 C++ 库参考中找到它。

您也可以尝试将外部参照函数转换为以函数模板作为参数(及其类型)的模板。不过,我从来没有尝试过这样的事情,无论如何都没有尝试过功能,所以我不知道它会带来多大的成功。它可能是你想要的,也可能不是。

如果您使用的是 c++0x,您将有更多选项可能会更符合您的意愿。

【讨论】:

  • 谢谢,我会看看我能做些什么来挖掘类型。我确实尝试过,但它到了我什至不确定我是否可以将函数模板作为争论,谷歌搜索并没有出现太多。
猜你喜欢
  • 1970-01-01
  • 2013-01-11
  • 2022-08-14
  • 1970-01-01
  • 1970-01-01
  • 2017-08-07
  • 2013-02-02
  • 2023-04-04
  • 2013-11-19
相关资源
最近更新 更多