如何编写可以使用const迭代器的C++ 11模板

Edw*_*ard 17 c++ templates c++11 c++-concepts

在回答关于CodeReview的这个问题时,我在思考如何编写模板函数来指示const包含对象的内容.

具体而言,请考虑这个模板化函数

#include <iostream>
#include <numeric>
#include <vector>

template <class It>
typename std::iterator_traits<It>::value_type average(It begin, It end) {
    typedef typename std::iterator_traits<It>::value_type real;
    real sum = real();
    unsigned count = 0;
    for ( ; begin != end; ++begin, ++count)
        sum += *begin;
    return sum/count;
}

int main()
{
    std::vector<double> v(1000);
    std::iota(v.begin(), v.end(), 42);
    double avg = average(v.cbegin(), v.cend());
    std::cout << "avg = " << avg << '\n';
}
Run Code Online (Sandbox Code Playgroud)

它需要一个迭代器并根据包含的数字计算平均值,但保证不通过传递的迭代器修改向量.如何将此传达给模板的用户?

请注意,声明如下:

template <class It>
typename std::iterator_traits<It>::value_type average(const It begin,
    const It end)
Run Code Online (Sandbox Code Playgroud)

不起作用,因为它不是迭代器,而是迭代器指向的东西,那就是const.我是否必须等待概念标准化?

请注意,我不想要求 const迭代器,而是指示它们可以在这里安全使用.也就是说,我想传达一个我的代码所做的承诺,而不是限制调用者:"我不会修改您的基础数据."

Ton*_*roy 10

template <class ConstIt>
Run Code Online (Sandbox Code Playgroud)

就这么简单.这里没有什么可以在调用方面强制执行,因为非const迭代器也可用于const访问,因此它只是API文档,这就是您选择的参数标识符--API文档.

这确实导致了被调用者/函数端的执行问题 - 所以它不能假装它只会使用迭代器进行const访问然后修改元素.如果你关心它,你可以使用一些标识符来接受参数,明确它不是要在整个函数中随处使用,然后创建一个const_iterator带有更方便标识符的版本.这可能很棘手,因为通常你不知道迭代器类型是否是容器的成员,更不用说容器类型是什么以及它是否也有const_iterator,所以某些概念确实是理想的 - 手指交叉C++ 14.与此同时:

  • 让你的来电者告诉你容器类型,
  • 写下你自己的特质,或者
  • 编写一个包含迭代器的简单包装器类,并确保只能const访问引用的数据来转义接口

下面说明了这个最后的包装器方法(并非所有的迭代器API都按照需要实现了这样的实现):

template <typename Iterator>
class const_iterator
{
  public:
    typedef Iterator                                                 iterator_type;
    typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
    // note: trying to add const to ...:reference or ..:pointer doesn't work,
    //       as it's like saying T* const rather than T const* aka const T*.
    typedef const typename std::iterator_traits<Iterator>::value_type& reference;
    typedef const typename std::iterator_traits<Iterator>::value_type* pointer;

    const_iterator(const Iterator& i) : i_(i) { }
    reference operator*() const { return *i_; }
    pointer operator->() const { return i_; }    
    bool operator==(const const_iterator& rhs) const { return i_ == rhs.i_; }
    bool operator!=(const const_iterator& rhs) const { return i_ != rhs.i_; }    
    const_iterator& operator++() { ++i_; return *this; }
    const_iterator operator++(int) const { Iterator i = i_; ++i_; return i; }
  private:
    Iterator i_;
};
Run Code Online (Sandbox Code Playgroud)

样品用法:

template <typename Const_Iterator>
void f(const Const_Iterator& b__, const Const_Iterator& e__)
{
    const_iterator<Const_Iterator> b{b__}, e{e__}; // make a really-const iterator
    // *b = 2;  // if uncommented, compile-time error....
    for ( ; b != e; ++b)
        std::cout << *b << '\n';
}
Run Code Online (Sandbox Code Playgroud)

在这里看到它在ideone.com上运行.