std::remove_if和std::isspace - 编译时错误

16

I have the following code:

#include <algorithm>
#include <cctype>
#include <string>

int main()
{
    std::string str;
    str.erase(std::remove_if(str.begin(), str.end(), std::isspace), str.end());
}

使用 MSVC-11.0 编译此代码时没有任何错误,但是 gcc 4.7.2 给出了以下错误:

main.cpp: In function ‘int main()’:
main.cpp:8:66: error: no matching function for call to ‘remove_if(std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)’
main.cpp:8:66: note: candidate is:
In file included from /usr/include/c++/4.7/algorithm:63:0,
                 from main.cpp:1:
/usr/include/c++/4.7/bits/stl_algo.h:1160:5: note: template<class _FIter, class _Predicate> _FIter std::remove_if(_FIter, _FIter, _Predicate)
/usr/include/c++/4.7/bits/stl_algo.h:1160:5: note:   template argument deduction/substitution failed:
main.cpp:8:66: note:   couldn't deduce template parameter ‘_Predicate’

我发现关于这个问题有这篇问题,但根据cppreference,没有任何一个带有两个参数的版本。我还发现了这篇问题,但是根据cppreference(是的,再次是它),我只看到了一个std::isspace函数重载。

谁是正确的?我做错了什么?如何修复它?


7
иҝҷжҳҜеҸҰдёҖдёӘе…ідәҺstd::isspaceзҡ„cppreferenceй“ҫжҺҘпјҡhttp://en.cppreference.com/w/cpp/locale/isspaceгҖӮдёҚе№ёзҡ„жҳҜпјҢcppreferenceзҡ„жҗңзҙўеј•ж“Һж— жі•жүҫеҲ°дёӨдёӘй“ҫжҺҘгҖӮ - dyp
1
@dyp 搜索功能没有,但是每个页面底部的“另请参阅”链接到另一个页面。 - Cubbi
4个回答

19

另一种std::isspace的重载方式,所以您需要指定要使用哪个。一种简单的方法是使用lambda表达式(或者如果您没有C++11支持,则编写自己的一行函数):

std::remove_if(str.begin(), str.end(), 
               [](char c){ 
                  return std::isspace(static_cast<unsigned char>(c));
               });

7

std::isspace是一个重载函数,尽管这两个重载位于不同的头文件中。请注意,您的代码可能会引入未定义的行为,因为仅可以将范围内的值0..UCHAR_MAX传递给std::isspace,而char可能被认为是有符号的。

以下是解决方案:

std::string str;
auto f = [](unsigned char const c) { return std::isspace(c); };
str.erase(std::remove_if(str.begin(), str.end(), f), str.end());

是的,is* 函数取值范围在 0 到 UCHAR_MAX 之间,而通常使用的 char 类型是有符号的,这种不匹配真的很糟糕。这使得 is* 函数基本上无用。 - Jan Hudec
1
据我所知,char的有符号性是一项实现细节。在我的工作中,我们使用gcc开关来保证它是无符号的,我想其他编译器也可能支持这个选项...但对于任何非ASCII字符来说都很麻烦(在Unicode的世界里...唉)。 - Matthieu M.
@MatthieuM:这是实现细节,但在所有编译器中默认为有符号,而函数只能使用无符号。任何试图重用的代码都不能依赖于编译器开关。 - Jan Hudec
@MatthieuM.:实际上,正确的方法似乎是std::isspace(std::char_traits<char>::to_int_type(c));这个函数总是将char转换为正整数,以便-1保留给eof。当然,在使用返回std::istream::traits_type::int_type的函数时不必这样做,因为流已经完成了。 - Jan Hudec
@JanHudec:太明显了 :/ C++需要认真改进其字符处理,因为它显示出它是在Unicode受到热捧之前创建的。 - Matthieu M.
@MatthieuM:C++中的Unicode并没有起到什么帮助,特别是因为语言环境支持通常并不实际有效。 - Jan Hudec

1
以下解决方案应该可以避免编译时错误:
str.erase(std::remove_if(str.begin(), str.end(), (int(*) (int)) std::isspace), str.end());

0

在C++ 11之后,您可以使用lambda函数(更易于理解),请参见以下内容:

string s = " 3/  2";

auto isSpace = [](const unsigned char c) 
{
    return std::isspace(c);
};
s.erase(remove_if(s.begin(), s.end(), isSpace), s.end());

输出:

3/2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接