基于范围的for,使用range_expression从std::vector返回非空项
Ranged-based for with range_expression returning non-null items from std::vector
考虑以下示例:
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
}
是否有可能在类内部创建一个函数来返回std::vector中的项也驻留在类中,但跳过nullptr项?或者其他任何与此相关的项目。我在想一些lambda函数的使用会使这个工作,但我不确定如何。
注意,我希望它在不重新创建任何其他新向量并返回它的情况下是有效的。应优先使用c++ 11
您可以使用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;})) {
// ...
}
如果您想挑战一下,这是一个更容易自己编写的范围适配器。您只需要一个迭代器类型,它所做的事情比++
对operator++()
的转发稍微复杂一些。
但是使用if
语句可能更容易,不是吗?
for (Item* i : foo.items) {
// either positive
if (i) {
// ...
}
// or negative
if (!i) continue;
// ...
}
这里有一种使用高阶函数和抽象过滤逻辑的lambda的替代方法。它不需要任何额外的依赖。
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`
});
通过在foo
中调用for_nonnull_items(this->items, /*...*/)
,你可以获得一个更好的接口:
foo.for_nonnull_items([](auto ptr)
{
// do something with `ptr`
});
从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
对另一个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有协程使这变得微不足道。代码大致如下:
std::generator<Item*> non_null_items() {
for( Item* i : items )
if ( i ) co_return i;
}
作为类中的方法。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- std::copy with return values - 防止"expression: string iterators incompatible"的更好方法?
- 使用 std::sort 时"invalid operands to binary expression"
- constexpr with std::array - "Non-type template argument is not a constant expression"
- 简单的模板化函数,用于转换 std::vectors - "illegal use of this type as an expression"