33

I am using std::ptr_fun as follows:

static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

as presented in this answer.

However this does not compile with C++17 (using Microsoft Visual Studio 2017), with the error:

error C2039: 'ptr_fun': is not a member of 'std'

How can this be fixed?

Barry
  • 286,269
  • 29
  • 621
  • 977
user3717478
  • 863
  • 1
  • 8
  • 15

7 Answers7

66

You use a lambda:

static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) {return !std::isspace(c);}));
    return s;
}

The answer you cited is from 2008, well before C++11 and lambdas existed.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    Compiles perfectly and it's easier to understand than the 2008 solution. Thanks. – user3717478 Jul 07 '17 at 14:51
  • I took the liberty of editing the 2008 answer with link to this one. – SergeyA Jul 07 '17 at 15:11
  • `s/int/unsigned char/`. – T.C. Jul 07 '17 at 15:33
  • @T.C.: [`isspace` takes an `int`](http://en.cppreference.com/w/cpp/string/byte/isspace). Not that this is in any way useful, but best to match the signature of the function in question. – Nicol Bolas Jul 07 '17 at 15:59
  • No, `unsigned char` here is required for correctness, since `string` uses (possibly signed) `char`, but `isspace`'s argument must be representable as `unsigned char` if it's not `EOF`. – T.C. Jul 07 '17 at 16:02
  • @T.C.: Whatever correctness problem would come from this function using `int` will come from `isspace`'s use of `int`. Or can you provide some code which would be OK if `isspace` were used directly but is wrong if there's an intermediate function? – Nicol Bolas Jul 07 '17 at 16:36
  • Well, yes, OP's code has the same bug, but I don't see why a glaring bug should be left uncorrected. YMMV. – T.C. Jul 07 '17 at 17:57
  • 2
    @T.C. I don't understand - `std::isspace()` takes an `int`. What difference does it make if the conversion to `int` happens into `isspace()` or into another function that just calls `isspace()`? – Barry Jul 07 '17 at 18:29
  • 5
    @Barry The point is that you aren't supposed to call any of the C character classification functions with a `char` or with a `char` converted (directly) to `int`. You are supposed to call them with a `char` converted to `unsigned char` and then to `int`. The intermediary `unsigned char` conversion is necessary to correctly handle negative-valued `char`s. – T.C. Jul 07 '17 at 18:34
  • 5
    @T.C. That's just a horrible interface then. Yay C. – Barry Jul 07 '17 at 18:43
  • It's worth noting that `std::isspace` takes (and returns) `int` because it's a C standard library relic from < 1990. When writing Actual C++™ you can `#include ` and then use [`std::isspace(' ', std::locale("C"))`](https://en.cppreference.com/w/cpp/locale/isspace), which as one might hope takes any character type and returns `bool`. – FeRD Mar 09 '21 at 00:59
20

Just use a lambda:

[](unsigned char c){ return !std::isspace(c); }

Note that I changed the argument type to unsigned char, see the notes for std::isspace for why.

std::ptr_fun was deprecated in C++11, and will be removed completely in C++17.

Barry
  • 286,269
  • 29
  • 621
  • 977
5

According to cppreference, std::ptr_fun is deprecated since C++11 and discontinued since C++17.

Similarly, std::not1 is deprecated since C++17.

So best don't use either, but a lambda (as explained in other answers).

Walter
  • 44,150
  • 20
  • 113
  • 196
4

Alternatively, you might use std::not_fn:

static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
        std::not_fn(static_cast<int(*)(int)>(std::isspace))));
    return s;
}
Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
4

My answer is similar to the already given answers in this thread. But instead of

int isspace(int c);

function from the standard C library, I am suggesting to use

bool isspace(char c, const locale& loc);

function instantiation from the standard C++ library (http://en.cppreference.com/w/cpp/locale/isspace), which is more type-correct. In this case you don't need to think about char -> unsigned char -> int conversions and about the current user's locale.

The lambda which searches for non-space will looks like this then:

[](char c) { return !std::isspace(c, std::locale::classic()); }

And the full code of ltrim function will look like this:

static inline std::string& ltrim(std::string& s) {
    auto is_not_space = [](char c) { return !std::isspace(c, std::locale::classic()); };
    auto first_non_space = std::find_if(s.begin(), s.end(), is_not_space);
    s.erase(s.begin(), first_non_space);
    return s;
}
Alexei Khlebnikov
  • 2,126
  • 1
  • 21
  • 21
1

You use Lambda as suggested by Nicol Bolas but you can use auto and type will be deduced there, as follow:-

    static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](auto c) {return 
       !std::isspace(c);}));
    return s;
  }
Owl66
  • 147
  • 12
0

use std::isspace with lambda instead of std::ptr_fun,

static inline std::string &ltrim(std::string &s) {
    str.erase(str.begin(), std::find_if(str.begin(), str.end(),
    [](unsigned char c) { return !std::isspace(c);}));
    return s;
}

you can find more details about std::isspace