c++ - 调整不可迭代容器以通过自定义模板化迭代器进行迭代

标签 c++ templates

我有一些类,由于各种原因超出了本次讨论的范围,我无法修改(省略了不相关的实现细节):

class Foo { /* ... irrelevant public interface ... */ };

class Bar {
  public:
    Foo& get_foo(size_t index) { /* whatever */ }
    size_t size_foo() { /* whatever */ }
};

(我正在处理许多类似的“Foo”和“Bar”类,它们都是从其他地方生成的代码和我不想子类化的东西,等等)

[编辑:澄清 - 尽管有许多类似的“Foo”和“Bar”类,但保证每个“外部”类都有 getter 和 size 方法。根据“内部”包含的类型,每个“外部”只有 getter 方法名称和返回类型不同。

所以,如果我有包含 Quux 实例的 Baz,就会有 Quux& Baz::get_quux(size_t index) 和 size_t Baz::size_quux()。]

考虑到 Bar 类的设计,您不能轻易地在 STL 算法(例如 for_each、find_if 等)中使用它,并且必须执行命令式循环而不是采用函数式方法(我更喜欢后者的原因也出来了本次讨论的范围):

Bar b;
size_t numFoo = b.size_foo();
for (int fooIdx = 0; fooIdx < numFoo; ++fooIdx) {
  Foo& f = b.get_foo(fooIdx);
  /* ... do stuff with 'f' ... */
}

所以...我从来没有创建过自定义迭代器,并且在阅读了关于 S.O. 的各种问题/答案之后。关于 iterator_traits 之类的,我想出了这个(目前半生不熟的)“解决方案”:

首先,自定义迭代器机制(注意:'function' 和 'bind' 的所有使用都来自 MSVC9 中的 std::tr1):

// Iterator mechanism...
template <typename TOuter, typename TInner>
class ContainerIterator : public std::iterator<std::input_iterator_tag, TInner> {
  public:
    typedef function<TInner& (size_t)> func_type;

    ContainerIterator(const ContainerIterator& other) : mFunc(other.mFunc), mIndex(other.mIndex) {}

    ContainerIterator& operator++() { ++mIndex; return *this; }

    bool operator==(const ContainerIterator& other) {
      return ((mFunc.target<TOuter>() == other.mFunc.target<TOuter>()) && (mIndex == other.mIndex));
    }

    bool operator!=(const ContainerIterator& other) { return !(*this == other); }

    TInner& operator*() { return mFunc(mIndex); }

  private:
    template<typename TOuter, typename TInner>
    friend class ContainerProxy;

    ContainerIterator(func_type func, size_t index = 0) : mFunc(func), mIndex(index) {}

    function<TInner& (size_t)> mFunc;
    size_t mIndex;
};

接下来,我获取表示内部容器开始和结束的有效迭代器的机制:

// Proxy(?) to the outer class instance, providing a way to get begin() and end()
// iterators to the inner contained instances...
template <typename TOuter, typename TInner>
class ContainerProxy {
  public:
    typedef function<TInner& (size_t)> access_func_type;
    typedef function<size_t ()> size_func_type;

    typedef ContainerIterator<TOuter, TInner> iter_type;

    ContainerProxy(access_func_type accessFunc, size_func_type sizeFunc) : mAccessFunc(accessFunc), mSizeFunc(sizeFunc) {}

    iter_type begin() const {
      size_t numItems = mSizeFunc();
      if (0 == numItems) return end();
      else return ContainerIterator<TOuter, TInner>(mAccessFunc, 0);
    }
    iter_type end() const {
      size_t numItems = mSizeFunc();
      return ContainerIterator<TOuter, TInner>(mAccessFunc, numItems);
    }

  private:
    access_func_type mAccessFunc;
    size_func_type mSizeFunc;
};

我可以按以下方式使用这些类:

// Sample function object for taking action on an LMX inner class instance yielded
// by iteration...
template <typename TInner>
class SomeTInnerFunctor {
  public:
    void operator()(const TInner& inner) {
      /* ... whatever ... */
    }
};

// Example of iterating over an outer class instance's inner container...
Bar b; /* assume populated which contained items ... */
ContainerProxy<Bar, Foo> bProxy(
  bind(&Bar::get_foo, b, _1),
  bind(&Bar::size_foo, b));
for_each(bProxy.begin(), bProxy.end(), SomeTInnerFunctor<Foo>());

根据经验,此解决方案可以正常运行(减去我在编辑上述内容时为简洁起见可能引入的任何复制/粘贴或拼写错误)。

最后,真正的问题是:

我不喜欢要求调用者使用 bind() 和 _1 占位符等。他们真正关心的是:外部类型、内部类型、外部类型获取内部实例的方法、外部类型获取计数内部实例的方法。

有没有办法以某种方式“隐藏”模板类主体中的绑定(bind)?我一直无法找到一种方法来分别为类型和内部方法分别提供模板参数...

谢谢!
大卫

最佳答案

您可以定义一个辅助模板结构来隐藏与 Foo 和 Bar 交互的实际机制。然后专门针对每个容器:

// incomplete general case
template <typename TOuter> struct LegacyContainerTraits;

// Specialization for 'Bar'
template <> struct LegacyContainerTraits<Bar>
{
    // The inner type of 'Bar' is 'Foo'
    typedef Foo inner_type;

    static size_t get_size(Bar const& outer) {return outer.size_foo();}
    static Foo& get_element(Bar const& outer, size_t index) {
        return outer.get_foo(index);
    }
};

// Specialization for Baz
template <> struct LegacyContainerTraits<Baz>
{
    // The inner type of 'Baz' is 'Quux'
    typedef Quux inner_type;

    static size_t get_size(Baz const& outer) {return outer.size_quux();}
    static Quux& get_element(Baz const& outer, size_t index) {
        return outer.get_quux(index);
    }
};

然后在 ContainerProxy/ContainerIterator 中,您只需存储对容器的引用/拷贝并调用 LegacyContainerTraits 的适当特化,而不是存储和使用函数。实际上,根本不需要 ContainerProxy:

template <typename TOuter> class LegacyContainerIterator;
template <typename TOuter> LegacyContainerIterator<TOuter> begin(TOuter& );
template <typename TOuter> LegacyContainerIterator<TOuter> end(TOuter& );

template <typename TOuter>
class LegacyContainerIterator : 
    public std::iterator<std::random_access_iterator_tag,
                         typename LegacyContainerTraits<TOuter>::inner_type >
{
private:
    ...
    friend LegacyContainerIterator<TOuter> begin<TOuter>(TOuter& );
    friend LegacyContainerIterator<TOuter> end<TOuter>(TOuter& );
    LegacyContainerIterator(TOuter& containerRef, size_t index) ... {};
    ...

public:
    ...
    typename LegacyContainerTraits<TOuter>::inner_type& operator*() {
        return LegacyContainerTraits<TOuter>
            ::get_element(mContainerRef, mIndex);
    }
    ...
};

template <typename TOuter>
LegacyContainerIterator<TOuter> begin(TOuter& containerRef)
{
    return LegacyContainerIterator<TOuter>(containerRef, 0);
}

template <typename TOuter>
LegacyContainerIterator<TOuter> end(TOuter& containerRef)
{
    return LegacyContainerIterator<TOuter>(
               containerRef,
               LegacyContainerTraits<TOuter>::get_size(containerRef));
}

然后您可以在循环或算法中相当轻松地使用自由函数。即使在基于范围的 for 循环中:

Bar b=...;

for (auto it=begin(b); it!=end(b); ++it) {...}

for (auto f : b) {...}

std::for_each(begin(b), end(b), ...);

更充实的版本:http://ideone.com/JA9hC

关于c++ - 调整不可迭代容器以通过自定义模板化迭代器进行迭代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11165341/

相关文章:

c++ - 将类型名称和值与模板包扩展混合

c++ - C++ 中模板化异常类的多重继承

c++ - 无法识别 GMock 宏? YCM 给我错误,但 Bazel 构建良好

c++ - 在任意数据类型上使用 <algorithm> 库

C++ 模板参数

c++ - 每当在 C++ 中调用函数时生成新类型

c++ - 模板参数默认值

c++ - unique_ptr 删除的 vector ?

c++ - 删除字符串算法中的重复项

c++ - 模板 - 这可以做到吗?