2013-03-25 49 views
21

Nếu các phần tử trong std::initializer_list luôn là giá trị const, tại sao chúng ta có phương thức mẫu như begin()/end() chứ không phải cbegin()/cend()? Tên này (theo quy ước, so sánh với ví dụ: std::vector) có thể gợi ý rằng cả hai phương pháp std::initializer_list đều có thể trả về iterator, khi chúng luôn trả về const_iterator.std :: initializer_list không có cbegin()/cend()

+2

Một câu hỏi hay. Làm tốt. –

+0

@LightnessRacesinOrbit Xin chào, rất vui được gặp bạn tại đây :-) – Des1gnWizard

+0

@ Des1gnWizard: Đây không phải là trang web hẹn hò, cảm ơn. –

Trả lời

25

Trong khi tôi không thể cung cấp một cái nhìn sâu sắc về lý do tại sao cbegin()cend() không nằm trong giao diện ngoài std::initializer_list 's đểbegin()end(), chắc chắn có lý do chính đáng tại sao hai hàm thành viên cuối cùng nên được đó.

Một lý do là, ví dụ, vòng lặp dựa trên phạm vi for được xác định bởi tiêu chuẩn C++ 11 chính xác về các hàm begin()end() (Đoạn 6.5.4/1). Vì vậy, để làm cho nó có thể sử dụng nó với danh sách khởi tạo, std::initializer_list có để cung cấp các begin()end() thành viên chức năng:

#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 << " "; 
    } 
} 

Hơn nữa, nó làm cho tinh thần để xem xét rằng chức năng thành viên cbegin()cend() không có mặt trước C++ 11: do đó, có begin()end() trên giao diện std::initializer_list cho phép thực hiện các thuật toán chung cũ được viết theo số begin()end() hoạt động với danh sách trình khởi tạo, mà không yêu cầu chúng được viết lại.

Bạn viết:

Những tên (theo quy ước, so với ví dụ std::vector) có thể gợi ý rằng cả hai std::initializer_list phương pháp có thể trở lại iterator, khi họ luôn luôn trả const_iterator.

Thực ra, sự tương tự này không phù hợp lắm. Ví dụ, hàm begin() của std::vector trả về số iterator khi được gọi trên một số không const của std::vector (tức là một biến thể có thể thay đổi, thêm và xóa) và const_iterator khi được gọi trên cá thể const (tức là một cái không thể thay đổi được, có nội dung không thể thay đổi được):

#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?"); 
} 

Danh sách khởi tạo là những tập hợp bất biến theo định nghĩa. Theo Đoạn 18.9/2 của tiêu chuẩn C++ 11:

Đối tượng loại initializer_list<E> cung cấp quyền truy cập vào một mảng đối tượng thuộc loại const E. [...]

Kể từ danh sách khởi tạo là bộ sưu tập của const yếu tố, các cbegin()cend() chức năng thực sự sẽ làm chính xác những điều tương tự mà begin()end() làm.

Trong thực tế, iteratorconst_iterator đều định nghĩa là con trỏ đến các yếu tố liên tục của kiểu giá trị trong danh sách khởi tạo, vì vậy nó là đáng tranh cãi cho dù đó là những trường hợp đó begin()end() luôn luôn trả const_iterator (như bạn giả định), hoặc cho dù họ luôn trả lại iterator.

Đây là cách Đoạn 18,9/1 của C++ 11 Tiêu chuẩn xác định các mẫu initializer_list lớp:

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 
    }; 

    // ... 
} 
+6

Tôi nhớ khi tôi đã từng kiếm tiền trên SO, nhưng sau đó một số người đến và trả lời tất cả các câu hỏi C++ tốt ở dạng xuất sắc . Bây giờ tôi chỉ có thể bình chọn. +1 – GManNickG

+0

@GManNickG: Tôi rất vui, và mặc dù tôi tin rằng bạn đang khen tôi nhiều hơn tôi xứng đáng, tôi rất vui khi nhận được sự hỗ trợ của bạn. Cảm ơn bạn. –

+2

@GManNickG: Chỉ cần nghĩ về tất cả những người C# đã từng là JonSkeeted kể từ ngày 1. – MSalters

Các vấn đề liên quan