基于Ranged的range_expression从std :: vector返回非空项

时间:2016-09-09 16:34:40

标签: c++ c++11 iterator

考虑以下示例:

(?:StatementId: \[?)(.*)(?:\]?, .*UserId: )([0-9]*)

是否可以在类中创建一个函数来返回同样驻留在类中的std :: vector中的项,但是跳过nullptr项?或任何其他项目。我在想一些lambda函数的使用会使这个工作,但我不知道如何。

注意我希望它在不重新创建任何其他新向量的情况下高效并返回。最好是在C ++ 11中工作。

4 个答案:

答案 0 :(得分:3)

您可以使用boost::adaptors::filtered(或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;})) {
    // ...
}

如果您想要挑战,这是更容易编写的适配器之一。你只需要一个迭代器类型,它比++的{​​{1}}前向稍微复杂一些。

但是使用operator++()语句可能更容易,不是吗?

if

答案 1 :(得分:2)

这是使用高阶函数和lambdas的替代方法,它抽象了过滤逻辑。它不需要任何额外的依赖。

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`
   });

通过调用for_nonnull_items(this->items, /*...*/)内的foo,您可以实现更好的界面:

foo.for_nonnull_items([](auto ptr)
   {
       // do something with `ptr`
   });

答案 2 :(得分:0)

std::iterator派生使自定义迭代器变得相当简单。在这种情况下,我实现了一个forward_only迭代器。根据需要添加更多功能。

#include <iterator>

template<class Iter>
struct non_null_forward_iterator : std::iterator<std::forward_iterator_tag, typename Iter::value_type>
{
    using value_type = typename Iter::value_type;

    non_null_forward_iterator(Iter i, Iter last) : iter_(i), last_(last)
    {
        seek();
    }

    value_type operator*() const {
        return *iter_;
    }

    non_null_forward_iterator& operator++() {
        ++iter_;
        seek();
        return *this;
    }

    void seek()
    {
        while (iter_ != last_) {
            if (*iter_)
                break;
            ++iter_;
        }
    }


    bool operator==(const non_null_forward_iterator& r) const {
        return iter_ != r.iter_;
    }

    bool operator!=(const non_null_forward_iterator& r) const {
        return iter_ != r.iter_;
    }

    Iter iter_;
    Iter last_;
};

template<class Container>
auto non_null_range(const Container& cont)
{
    using underlying_iter_type = typename Container::const_iterator;
    using iter_type = non_null_forward_iterator<underlying_iter_type>;

    struct X {

        iter_type begin() const { return begin_; }
        iter_type end() const { return end_; }

        iter_type begin_;
        iter_type end_;
    };

    return X {
        iter_type(cont.begin(), cont.end()),
        iter_type(cont.end(), cont.end())
    };
}

struct Item {};
std::ostream& operator<<(std::ostream& os, const Item& item)
{
    return std::cout << "an item";
}

int main()
{

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

    for (auto p : non_null_range(items))
    {
        std::cout << *p << std::endl;
    }
}

预期产出:

an item
an item

答案 3 :(得分:0)

This answer另一个SO问题提供了一个适用于此处的解决方案。这个问题的公认答案没有。

这在C ++ 14中实现了一个通用的过滤器帮助器。

for( auto ptr: filter([](auto&& x){return x!=nullptr;})( test ) )

将遍历test的元素,使x!=nullptr

在C ++ 11中执行此操作主要包括填写返回类型。最后的filter函数是例外,它返回一个lambda,这在C ++ 11中是不可能的,并且在lambda中使用auto&&

许多C ++ 11编译器支持lambdas的auto参数。那些没有的,你可以将返回的filter lambda及其参数折叠到filter函数本身,然后写出繁琐的返回值。

C ++ 2x TS具有协同作用,这使得这一点变得微不足道。 The code will look roughly like

std::generator<Item*> non_null_items() {
  for( Item* i : items )
    if ( i ) co_return i;
}

作为班上的一种方法。

这反映了类似的C#和python语法。