0

我正在尝试从字符串中删除空格

line.erase(remove_if(line.begin(), line.end(), isspace), line.end()); 

但是 Visual Studio 2010 (C++ Express) 告诉我

1   IntelliSense: no instance of function template "std::remove_if" matches the argument list   d:\parsertry\parsertry\calc.cpp 18

完整来源

这是为什么?一段简单的代码

int main() {
    string line = "hello world   111    222";
    line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
    cout << line << endl;

    getchar();

    return 0;
}

验证功能是否有效?

有趣的是,尽管如此,它运行时给出了正确的结果。

4

3 回答 3

1

不要质疑 Intellisense,有时最好忽略它。解析器或数据库不知何故搞砸了,所以它不再正常工作了。通常,重新启动将解决问题。

如果您真的想知道代码是否格式错误,那么只需按 F7 即可编译。

于 2012-10-25T13:26:05.380 回答
0

您的源代码使用 Visual C++ 11.0(Visual Studio 2012 附带的编译器)编译时甚至没有警告。

Intellisense 使用自己的规则,并不总是可靠的。

也就是说,您对除原始 7 位 ASCII 之外的所有字符集使用的isspace未定义行为。这意味着您从中获得的高度赞成的答案只是胡说八道(这不足为奇)。您需要将参数转换为(C 库的)isspaceunsigned char避免负值和 UB。

C99 §7.4/1(来自 N869 草案):

头文件<ctype.h>声明了几个对测试和映射字符有用的函数。在所有情况下,参数都是 an int,其值应表示为 anunsigned char或应等于宏的值EOF。如果参数有任何其他值,则行为未定义。

包装 C 函数的一种简单方法是

bool isSpace( char const c )
{
    typedef unsigned char UChar;
    return !!::isspace( UChar( c ) );
}

为什么typedef

  1. 当您已经拥有这样的 时,它使代码更容易适应typedef,这并不少见;

  2. 它使代码更清晰;和

  3. 它避免了 C 语法转换,从而在通过正则表达式或其他模式匹配进行搜索时避免误报。

但是,为什么!!(否定运算符的双重应用)?考虑到从to的自动隐式转换?而且,如果有人绝对认为转换应该是明确的,那不应该是 a ,而不是?intboolstatic_cast!!

好吧,!! 避免了来自 Visual C++ 编译器的愚蠢警告,

“警告 C4800: 'int' : 强制值 bool 'true' 或 'false' (性能警告)”

并且 astatic_cast不会停止该警告。最好的做法是消除该警告,并且由于 Visual C++ 是最常用系统(即 Windows)上的主要 C++ 编译器,因此最好在所有可移植的代码中执行此操作。

哦,好的,但是,既然函数无论如何都必须被包装,那么……为什么要使用旧的 C 库isspace(单参数)函数,而<locale>头文件提供了一个更灵活的 C++(两个参数)isspace函数?

嗯,首先,旧的 Cisspace函数是问题中使用的函数,所以这就是这个答案中讨论的函数。我专注于讨论如何不错误地执行此操作,即如何避免未定义的行为。讨论如何正确地做这件事会把它带到一个完全不同的层次。

但是在实践中,可以认为同名的 C++ 级函数已被破坏,因为直到最近使用 g++ 编译器(甚至可能使用 g++ 4.7.2,我最近都没有检查过)只有 C 语言环境机制在 Windows 中有效,而 C++ 一级没有。它可能已经修复,因为 g++ 现在支持宽流,我不知道。无论如何,那里的 C 库isspace函数,除了在实践中更便携和通常在 Windows 中工作之外,还更简单,我相信,更高效(尽管为了提高效率,如果它被认为很重要,应该总是 MEASURE!)。

感谢 James Kanze 在评论中提出(基本上)上述问题。

于 2012-10-25T13:28:59.413 回答
0

是什么isspace?根据包含的头文件和您使用的编译器,您的代码甚至可能无法编译。(我不了解 IntelliSense,但它可能正在查看所有标准标头,并看到歧义。)

标准中有两个isspace功能,一个是模板。将函数模板传递给另一个函数模板的模板参数并没有为编译器提供足够的信息来进行模板参数推导:为了解决 的重载isspace,它必须知道 期望的类型 remove_if,它只模板参数推导成功后才知道。并且要对 进行模板参数推导remove_if,它必须知道参数的类型,这意味着 的类型isspace,它只有在能够解决它的重载时才会知道。

(我真的很惊讶你的一点代码编译:你显然包含<iostream>,并且通常<iostream>会包含 <locale>,这将引入函数模板isspace。)

当然,函数模板isspace必须用两个参数调用,所以如果它被选择,实例化remove_if 不会编译(但编译器在 remove_if选择一个函数之前不会尝试实例化)。如果传递 a , isspacein <ctype.h>将导致未定义的行为char,因此您不能使用它。通常的解决方案是为您的工具箱创建一组谓词对象,并使用它们。如果您只关心以下内容,则应该可以使用char

template <std::ctype<char>::mask m>
class Is : public std::unary_function<char, bool>
{
    std::locale myLocale;  //  To ensure lifetime of following...
    std::ctype<char> const* myCType;
public:
    Is( std::locale const& loc = std::locale() )
        : myLocale( loc )
        , myCType( &std::use_facet<std::ctype<char> >( myLocale ) )
    {
    }

    bool operator()( char ch ) const
    {
        return myCType->is( m, ch );
    }
};
typedef Is<std::ctype_base::space> IsSpace;

添加额外的 typedef 很简单,因此您可以获得完整的集合,而且我发现添加IsNot模板也很有用。这很简单,它避免了所有周围的问题。

于 2012-10-25T14:55:31.427 回答