Adam Hunyadi
Adam Hunyadi

Reputation: 1952

Function returning its argument or checking for nullptr

I would like to loop on a vector and filter out all the non-null-pointer elements. I'm looking for either an std function that checks for something being a nullptr or an std function that actually returns whatever is passed to it (like std::forward), since a null pointer would evaluate to false.

std::copy_if(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    std::is_pointer<ObjectType*>); // This does not compile

std::copy_if(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    std::forward<ObjectType*>); // This does not compile either

std::copy_if(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    static_cast<bool>); // This won't help me :)

std::copy_if(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    [] (const auto a) { return a; } ); // This is awkward

Upvotes: 3

Views: 247

Answers (3)

Barry
Barry

Reputation: 303186

There is nothing like this directly in the standard library. Conceptually, the thing you'd want is:

std::copy_if(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    bool);

assuming you could use type names as factory functions that construct instances of that type. But, that's not a thing you can do in C++. At least directly. We can write that:

template <class T>
struct factory {
    template <class... Args>
    T operator()(Args&&... args) const {
        return T(std::forward<Args>(args)...);
    }
};

std::copy_if(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    factory<bool>{});

If you don't like typing {}s, you could shorten this by making a variable template.


That said, the problem with this:

[](const auto a) { return a; } ); // This is awkward

isn't so much that it's awkward as much as it's inefficient - that's two copies (one in, one out). You'd want:

[](const auto& a) { return static_cast<bool>(a); }

Or really just force the bool conversion earlier:

[](bool b){ return b; }

Upvotes: 1

T.C.
T.C.

Reputation: 137345

Stuck with the stuff in std, you can use std::remove_copy_if with std::logical_not.

std::remove_copy_if(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    std::logical_not<ObjectType*>()); // or std::logical_not<> in C++14

Or, you can use remove_copy passing nullptr:

std::remove_copy(dynamicObjects.begin(), dynamicObjects.end(),
    std::back_inserter(existingObjects), 
    nullptr);

If you really like copy_if, you can use not_fn or not1 on the logical_not instead.

Upvotes: 5

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275585

Nope.

I do find having identity lying around to be useful sometimes:

struct identity_t {
  template<class T>
  T operator()(T&& t)const{ return std::forward<T>(t); }
  constexpr identity_t() {}
};
constexpr identity_t identity;

(it converts rvalues to copies, for reference lifetime extension)

Mainly I use it when writing functions with optional mappings, like a transform-filter function: identity is the default for transform, and always_true is the default for filter.

Upvotes: 2

Related Questions