【问题标题】:C++ non-iterator based range library?基于 C++ 非迭代器的范围库?
【发布时间】:2011-06-01 07:28:18
【问题描述】:

我对 stl 迭代器的笨拙感到沮丧,我正在寻找更有用的东西。特别是一个更容易映射和过滤的概念,也更容易实现:基本上是 C#/python/ruby/everything-but-C++ 风格的枚举。

我看到了 Andrei Alexandrescu 在 2009 年的 Iterator's must go! boostcon 主题演讲,他在其中描述了一个范围概念,这几乎正是我正在寻找的东西,甚至更多。

有人知道这样的事情是否真的实现了吗?我知道 boost::range,但这不是一个理想的解决方案;它是根据迭代器实现的(这更令人困惑和复杂,效率更低,通用性更低,并且使编写自己的迭代器至少与实现迭代器一样混乱)。不过,总比没有好。还有什么更好的吗?

编辑:关于为什么这很有吸引力的讨论很多。 On iteration 更清楚地解释了动机。我意识到与 D 的链接——但这不应该分散争论本身的注意力。

【问题讨论】:

  • 我建议你对安德烈的演讲多加怀疑,他有自己的动机。我同意他提出了一些好的观点,但斯捷潘诺夫也有他的好观点。范围并不总是直观的。您关于提升范围的大多数声明都是没有根据的。另请记住,您无法对复杂的世界进行建模。
  • @ybungalobill:也许吧,但我已经厌倦了迭代器,可以尝试一下。而 boost::range 真的不完全是(我不知道你所说的 unbased 是什么意思):它需要你实现迭代器,从而维护 both 迭代器 and 的代码i> 范围 - 我正在寻找更简单的东西,而不是更复杂的东西。最后,许多其他语言已经成功地使用了类似范围的概念这一事实很好地暗示了这并不是一些遥远的不切实际的概念。我正在寻找 C++ 枚举器(不是迭代器),andrei 提出了更好的东西:有什么不喜欢的?
  • @Eamon,为什么范围会比迭代器更简单?即使您不将它们实现为 2 个迭代器,它们基本上仍然是 2 个 const 迭代器。在某些情况下,它们可能更方便使用,但是为什么它们的非迭代器实现会比基于迭代器的更简单?
  • 我不同意你们所有人的观点:迭代器灵活的。
  • @Eamon Nerbonne:迭代器转换(过滤、用函数组合、获取指向结果的成员指针......)和“特殊”输出迭代器(除了最后一个你给他们的东西之外,它会忘记所有的东西) ,或存储修改/过滤的版本)正是使它们变得灵活的原因。 灵活的是标准的迭代器类:即。无法区分右值/左值迭代器。我找到了一个 C++0x 提案来解决这个问题。借助 auto 和 lambda,它们是使用运行时高效的表达函数构造来增强 C++ 的绝佳工具。

标签: c++ boost stl iterator range


【解决方案1】:

自己做起来似乎很容易——假设你不介意一些工作/打字

我还没有编译这个,但是这样的东西应该会让你有点前进

template< typename T>
class enumeration : boost::noncopyable {
virtual T& next() = 0;
virtual bool has_next() = 0;
};

template< typename T>
class editable_enumeration : public enumeration<T> {
virtual void remove() = 0;
}

//That was simple enough, now for adapting the std containers we
//will use the iterator interface already exposed. For new classes,
//we can implement iterators in any way we want. (e.g. copy python or java)

template < class C >
class const_std_enumeration : public enumeration<C::value_type>
{
protected:
C::const_iterator iter_;
C::const_iterator end_;

public:
typedef C::value_type value_type;

const_std_enumeration( C const& c) :
iter_(c.begin()), end_(c.end()) { } //c++0x use std::begin(c), std::end(c) instead

virtual value_type& next() { if(iter_!=end_) return *iter_++; throw std::runtime_error("No more elements"); }
virtual bool has_next() { return iter_!=end_; }
};

template < class C>
class std_enumeration : public enumeration<C::value_type>
{
protected:
C& c_;
C::iterator iter_;
C::iterator end_;

public:
typedef C::value_type value_type;

std_enumeration( C& c) :
c_(c), iter_(vector.begin()), end_(vector.end()) { }

virtual value_type& next() { if(v_!=end_) return *iter_++; throw std::runtime_error("No more elements"); }
virtual bool has_next() { return iter_!=end_; }
virtual remove() { iter_ = c_.erase(iter_); }
};


//Since we can't add methods to std containers, we will use an
//overloaded free-function `enumeration` to get enumerations from ANY container
//We could use `auto_ptr` or `unique_ptr`, but for simplicity's sake, I'm
//just using raw pointers

template < class C >
editable_enumeration<C::value_type>* enumeration( C&c ) { return new std_enumeration<C>(c); }

template < class C >
enumeration<C::value_type>* enumeration( C const& c ) { return new const_std_enumeration<C>(c); }

对于所有其他容器,只需确保已定义 enumeration 并返回枚举或可编辑枚举。如果您的其他容器已经实现了迭代器接口,那么这应该可以工作

我们现在可以写:

template<typename T>
bool contains( enumeration<T>* e, T const& t) {
while(e->has_next())
  if ( t == e->next() )
    return true;
return false;
}
...
std::vector<int> v = getIntVector();
if( contains( enumeration(v), 10 ) ) std::cout<<"Enumeration contains 10\n";
std::list<int> l = getIntList();
if( contains( enumeration(l), 10 ) ) std::cout<<"Enumeration contains 10\n";

应该指出,迭代器概念的一个巨大优势是,当has_next() 返回false 时请求下一个元素。对于迭代器,end()++ 是 (IIRC) 未定义的行为。对于枚举,它被定义为throw std::runtime_error(...),这对某些人来说可能更糟。

【讨论】:

  • 未定义的行为并不比异常好:它可能是异常;或者可能是很久以后在不确定的时间出现的异常(或访问冲突)。
  • 感谢您的实施!然而,这不是我想要的。这样的事业需要成为一个更大的、经过测试的图书馆的一部分。毕竟algorithm 的大部分内容都需要重新实现,而且高性能不是给定的(特别是,您在这里使用了虚拟调度)。
  • 为什么要通过指针而不是按值返回枚举数?这为内存泄漏和意外行为打开了大门(contains 不是只读检查,它会修改范围?非常违反直觉!)
  • @Eamon Nerbonne 我使用虚拟调度的原因是将类型(enumerationeditable_enumeration)从它们迭代的容器中解耦:使用 OO 设计而不是通用算法设计。 IMO,如果您想要高效率,请使用迭代器。
  • @Kitsune:我喜欢你的迭代器包装理念 :-)。您仍然希望重新实现这些函数,即使它只是在 STL 方面使用包装器清理 api 并调整一些函数的语义以更加适应范围。
【解决方案2】:

也许这个对你有帮助:http://rangelib.synesis.com.au/

【讨论】:

    【解决方案3】:

    已经有几年了,但现在似乎有一个库可以实现正确的功能样式序列:C++ Streams

    来自项目主页的代码示例:

    将前 10 个方格相加:

    int total = stream::MakeStream::counter(1)
        .map([] (int x) { return x * x; })
        .limit(10)
        .sum();
    

    【讨论】:

    • 虽然 D 中的范围也可以转发随机访问和其他可能的特性,这在某些情况下保证了更好的性能。
    猜你喜欢
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 1970-01-01
    • 2020-04-03
    • 2011-10-20
    • 1970-01-01
    相关资源
    最近更新 更多