Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ranged-based for with range_expression returning non-null items from std::vector

Consider the following example:

class Foo {
public:
    std::vector<Item*> items = { nullptr, new Item(), new Item(), nullptr };

    // function to return all non-nullptr items as an iterator
};

int main() {
    Foo foo;

    for (Item* i : foo.functionToReturnIteratorOverAllNullItems)
        // do something
}

Is it possible to create a function inside the class to return the items in a std::vector also residing in the class, but skipping nullptr items? Or any other items for that matter. I am thinking some lambda function usage would make this work but I am not sure how.

Note I want it to be efficient without re-creating any other new vector and return that. Should preferably work in C++11.

like image 439
user1853417 Avatar asked Jul 27 '26 16:07

user1853417


2 Answers

You could use boost::adaptors::filtered (or ranges::view::filter):

// pipe version
for (Item* i : foo.items | filtered([](Item* i){return i;})) {
    // ...
}

// function version
for (Item* i : filter(foo.items, [](Item* i){return i;})) {
    // ...
}

This is one of the easier range adaptors to write yourself if you want a challenge. You just need an iterator type that does something slightly more complicated than forward along ++ for operator++().


But it's probably easier to just use an if statement, no?

for (Item* i : foo.items) {
    // either positive
    if (i) {
       // ...
    }

    // or negative
    if (!i) continue;
    // ...
}
like image 122
Barry Avatar answered Jul 30 '26 05:07

Barry


Here's an alternative approach using higher-order functions and lambdas that abstracts the filtering logic. It does not require any additional dependency.

template <typename TContainer, typename TF>
auto for_nonnull_items(TContainer&& container, TF f)
{
    for(auto&& i : container)
    { 
        if(i == nullptr) continue;
        f(i);
    }

    return f;
}

std::vector<int*> example{/*...*/};
for_nonnull_items(example, [](auto ptr)
   {
       // do something with `ptr`
   });

By calling for_nonnull_items(this->items, /*...*/) inside foo you can achieve a nicer interface:

foo.for_nonnull_items([](auto ptr)
   {
       // do something with `ptr`
   });
like image 38
Vittorio Romeo Avatar answered Jul 30 '26 05:07

Vittorio Romeo