没有cbegin()/cend()的std::initializer_list

25

如果std::initializer_list中的元素始终是const值,那么我们为什么会有像begin()/end()这样的模板方法而不是cbegin()/cend()?这些名称(按照约定,与例如std::vector相比)可能表明,两种std::initializer_list方法都可以返回iterator,但它们始终返回const_iterator


2
一个好问题。干得好。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit 嘿美女,很高兴在这里见到你 :-) - Des1gnWizard
@Des1gnWizard:这不是一个约会网站,谢谢。 - Lightness Races in Orbit
@LightnessRacesinOrbit _ _.... 好的,你是对的... _ _.... - Des1gnWizard
1个回答

26

尽管我无法解释为什么cbegin()cend()没有作为std::initializer_list接口的一部分附加begin()end(),但有充分的理由说明为什么应该存在最后两个成员函数。

例如,其中一个原因是基于范围的 for 循环是由C++11标准精确地定义为函数begin()end()(第6.5.4/1段)。因此,为了使其能够与初始化列表一起使用,std::initializer_list必须提供begin()end()成员函数:

#include <utility>
#include <iostream>

int main()
{
    auto l = { 1, 2, 3, 4, 5 };
    for (int x : l) // Works because std::initializer_list provides
                    // the member functions begin() and end().
    {
        std::cout << x << " ";
    }
}
此外,考虑到成员函数 cbegin()cend() 在 C++11 之前不存在, 因此在 std::initializer_list 接口中有 begin()end() 允许旧的通用算法在不需要重写的情况下也可以使用初始化列表,而这些算法是按照 begin()end() 这样的术语编写的。

你写道:

  

通过比较例如 std::vector,这些名称(按约定)可能会暗示两个方法都会返回iterator,但实际上它们总是返回const_iterator

实际上,这个类比并不十分恰当。例如,对于一个非conststd::vector(即可修改、添加和删除其元素的可变实例),begin()函数会返回一个iterator,而对于一个const的实例(即其内容不能被更改的不可变实例),begin()函数会返回一个const_iterator

#include <vector>
#include <type_traits>

int main()
{
    // A non-const vector...
    std::vector<int> v = { 1, 2, 3, 4, 5 };

    auto i = v.begin();
    static_assert(
        std::is_same<decltype(i), decltype(v)::iterator>::value, 
        //                                     ^^^^^^^^
        //                                     ...non-const iterator!
        "What?");

    // A const vector...
    std::vector<int> const vc = { 1, 2, 3, 4, 5 };
    auto ic = vc.begin();
    static_assert(
        std::is_same<decltype(ic), decltype(vc)::const_iterator>::value,
        //                                       ^^^^^^^^^^^^^^
        //                                       ...const iterator!
        "What?");
}

按照C++11标准第18.9/2段的规定,初始化器列表是不可变集合:

类型为 initializer_list<E> 的对象提供对类型为 const E 的对象数组的访问。[...]

由于初始化器列表是由const元素组成的集合,因此cbegin()和函数实际上执行与begin()end()相同的操作。

事实上,iteratorconst_iterator 都定义为指向初始化器列表值类型的常量元素的指针,因此可以争论begin()end()是否总是返回const_iterator(如你所假设)或始终返回iterator

C++11标准的第18.9/1段规定了initializer_list类模板:

namespace std {
    template<class E> class initializer_list {
    public:
        typedef E value_type;
        // ...
        typedef const E* iterator;
        typedef const E* const_iterator;
        // ...
        constexpr const E* begin() const noexcept; // first element
        constexpr const E* end() const noexcept; // one past the last element
    };

    // ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接