【问题标题】:What is the purpose of the const overloads of std::begin and std::end?std::begin 和 std::end 的 const 重载的目的是什么?
【发布时间】:2016-02-08 17:39:16
【问题描述】:

对于std::begin,我们有两个容器重载:

template< class C > 
auto begin( C& c ) -> decltype(c.begin());
template< class C > 
auto begin( const C& c ) -> decltype(c.begin());

但是C 的常量可以通过通常的模板推导规则推导出来,所以看起来第二个重载是多余的。我错过了什么?

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    在右值上调用begin(和end)是合理的,前提是我们在容器被销毁后不使用生成的迭代器。但是,将右值传递给 T&amp; 形式的参数将不起作用,这是第二个重载发挥作用的地方。

    但是,我们很可能正在处理对前range-based for proposal wording 的轻率转换:

    将以下内容添加到 [container.concepts.member] 的末尾

    template<Container C> concept_map Range<C> {
        typedef C::iterator iterator;
        iterator begin( C& c ) { return Container<C>::begin(c); }
        iterator end( C& c )   { return Container<C>::end(c); } };
    
    template<Container C> concept_map Range<const C> {
        typedef C::const_iterator iterator;
        iterator begin( const C& c ) { return Container<C>::begin(c); }
        iterator end( const C& c )   { return Container<C>::end(c); } };
    

    当明确概念不会进入 C++11 时,论文被修改,所有四个函数模板都可能被翻译成等效的命名空间范围的函数模板。这产生了(可能是意外的)右值被接受的后果,而原始代码只是为了区分不同限定的容器类型。

    请注意,begin/end 的现代实现将改为使用转发引用 - 例如

    template <typename T>
    constexpr auto begin(T&& t)
      -> decltype(std::forward<T>(t).begin()) {
        return    std::forward<T>(t).begin();
    }
    

    【讨论】:

    • 我不太相信这是有意的设计。从容器中获取begin() 而无法从同一个容器中获取end() 似乎没有用。
    • @T.C. …除非你知道它的长度上限。不管怎样,我正在努力追查那篇论文。
    • 当然,除了数组右值没有重载,其长度实际上在类型中,因此在调用站点肯定是已知的。就个人而言,我怀疑这是改编旧的container concept maps 的产物,旧的const 和非const 版本分别使用const_iteratoriterator
    • @T.C. AFAICS,右值数组是一个疏忽(很少使用)。在这方面没有 LWG 问题?
    • 我的理论是他们把它放进去是因为它在概念被删除之前就在那里(并且在概念被删除之前因为使用了 iterator/const_iterator),然后在删除后清理他们只是应用了从概念版本到无概念版本的准机械翻译。
    猜你喜欢
    • 2012-07-02
    • 2012-12-04
    • 1970-01-01
    • 2020-09-22
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多