【问题标题】:Overloading functions not compiling重载函数不编译
【发布时间】:2010-09-09 19:16:19
【问题描述】:

我正在关注 O'Reilly 的 C++ Cookbook 一书,我尝试了其中一个示例,代码如下:

#include <string>
#include <iostream>
#include <cctype>
#include <cwctype>

using namespace std;

template<typename T, typename F>
void rtrimws(basic_string<T>& s, F f){
    if(s.empty())
        return;

    typename basic_string<T>::iterator p;
    for(p = s.end(); p != s.begin() && f(*--p););

    if(!f(*p))
        p++;

    s.erase(p, s.end());
}

void rtrimws(string& ws){
    rtrimws(ws, isspace);
}

void rtrimws(wstring& ws){
    rtrimws(ws, iswspace);
}

int main(){
    string s = "zing            ";
    wstring ws = L"zonh     ";

    rtrimws(s);
    rtrimws(ws);

    cout << s << "|\n";
    wcout << ws << "|\n";
}

当我尝试编译它时,我收到以下错误

trim.cpp: In function ‘void rtrimws(std::string&)’:
trim.cpp:22: error: too many arguments to function ‘void rtrimws(std::string&)’
trim.cpp:23: error: at this point in file

我不明白出了什么问题。如果我不使用 char 版本(字符串)而只使用 wchar_t 版本,一切运行顺利。

顺便说一句,我在 64 位 ubuntu 机器上使用 g++ 4.4.3

【问题讨论】:

  • 顺便说一句,它在 VC++ 上编译得很好
  • 在关闭这个帖子之前,为了进一步学习,有一些事情我想讨论一下。该代码当然可以在 VC++ 中编译,我尝试过没有任何问题,但在 GCC 中没有。函数 isspace 在 cctypes.h 中声明为 __exctype (isspace);这真的是 extern int isspace(int) throw ()

标签: c++ templates stl


【解决方案1】:

isspace 也是 C++ 中的一个模板,它接受一个模板化的字符和一个使用 facet std::ctype&lt;T&gt; 对给定字符进行分类的语言环境(因此它无法决定采用哪个版本,因此忽略模板)。

尝试指定您的意思是 C 兼容版本:static_cast&lt;int(*)(int)&gt;(isspace)。编译器之间的差异可能来自编译器之间对重载函数名称的推导处理不一致 - 请参阅clang PR。请参阅 Faisal 的第一组测试用例中的第二个案例以获取类似案例。


有人在 IRC 上指出,此代码将使用 char 调用 isspace - 但 isspace 采用 int 并要求给定的值在 unsigned char 值或 EOF 的范围内。现在,如果 char 在您的 PC 上签名并存储负的非 EOF 值,这将产生未定义的行为。

我建议像@Kirill 在评论中所说的那样做,只使用模板化的std::isspace - 然后你也可以摆脱函数对象参数。

【讨论】:

  • 我试图寻找 isspace 的定义,它看起来不像一个模板。它实际上是用扩展为“extern int isspace(int) throw ()”的 marco __exctype 定义的
  • @Sambatyon 那么你正在寻找错误的文件。无论如何,您已经接受了一个尝试/猜测答案:“尝试 xyz 也许它有效。但不要问为什么!!”。应该避免这些没有给出理由的答案。因为下次你遇到这个问题时,你尝试他的解决方法,也许它不起作用,然后你不能自己解决它,因为你不知道为什么首先会出现问题。
【解决方案2】:

试试rtrimws(ws, ::isspace);

另外,请注意,您应该使用反向迭代器。

【讨论】:

  • 不保证 cctype 标头声明 ::isspace,仅声明 std::isspace。这里给出的解决方案是脆弱的。
  • 那么唯一的解决方案就是不使用using namespace std;
  • 在函数范围之外,当然应该避免“使用命名空间std”。
【解决方案3】:

那是因为isspace 是 c++ 中的模板函数。它不能推导出F。如果您想使用 isspace 的 C 变体,您可以完全限定其名称,如下所示:

void rtrimws(string& ws){
    rtrimws(ws, ::isspace); // this will use isspace from global namespace
                            // C++ version belongs to the namespace `std`
}

这是另一个很好的示例,为什么您不应该使用 using namespace std

【讨论】:

  • 请注意,当您使用::isspace 时,您需要包含ctype.h。未指定使用 cctype 之类的 C++ 标头时是否声明全局名称。
  • 同意。实际上不需要传递isspace 作为参数。 OP 可以在 rtrimws 模板中使用模板化 C++ 版本的 isspace 函数。
  • 除非我会失去这个学习新东西的机会:(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-10
  • 2013-06-24
相关资源
最近更新 更多