2012-12-06 44 views
13

Tại sao không std::pair có trình lặp?Tại sao không std :: pair có iterators?

std::pair phải cung cấp iteratorconst_iterator cũng như begin()end() - chỉ dành cho hai thành viên của họ.

Tôi nghĩ điều đó sẽ hữu ích vì sau đó chúng tôi có thể chuyển chúng vào các chức năng được lập biểu mẫu mà mong đợi các lần lặp, như vector hoặc set.

Có bất kỳ nhược điểm nào đối với điều này không?

+10

'std :: pair '. Iterator chỉ hoạt động trên các thùng chứa đồng nhất. –

+0

Mặc dù 'std :: pair' đôi khi được sử dụng như một phạm vi, không có gì về nó đòi hỏi hoặc thậm chí khuyến khích sử dụng như vậy - vì vậy chuyên cho rằng sẽ không được quá lớn. Một dự thảo ban đầu của C++ 11 thực sự đã có điều này tại một thời điểm. Xem http://stackoverflow.com/questions/6167598/why-was-pair-range-access-removed-from-c11 –

+2

Nếu bạn thấy mình muốn truyền đối tượng của bạn đến các chức năng được tạo khuôn mẫu như vậy thì có lẽ bạn nên sử dụng 'std: : mảng 'thay vì' std :: pair '. – leftaroundabout

Trả lời

22

Một lý do là hai thành phần của một cặp có thể có các loại khác nhau. Điều này không phù hợp với mô hình trình lặp.

Điều tương tự cũng xảy ra đối với bộ dữ liệu, nơi có trình lặp có lẽ còn hấp dẫn hơn.

Nếu bạn cần đồng nhất vùng chứa có độ dài cố định, bạn có thể sử dụng std::array<T, n>.

+0

Các std đã std :: cặp std :: equal_range – QuentinUK

+0

@QuentinUK Nhận xét của bạn không hữu ích cho những người trong chúng ta, những người không biết điều đó. – Andrew

+0

Tôi đã đưa ra một lời giải thích dài hơn nhưng không thể trong số lượng ký tự giới hạn được cho phép trong nhận xét. Có thể ít nhất Google có thêm thông tin. – QuentinUK

2

Mục đích của std::pair không phải là một thùng chứa truyền thống mà là để phục vụ như một bộ cho phép hai đối tượng không đồng nhất có khả năng được coi là một đối tượng duy nhất.

Ngoài ra, vì bạn có quyền truy cập trực tiếp vào cả hai phần của cặp và vì các loại được ghép nối có thể không giống nhau, nên trình lặp sẽ không có ý nghĩa.

1

Tôi không nghĩ có bất kỳ nhược điểm nào khác ngoài nó chỉ hoạt động cho pair<T,T>, không phải pair<T,U>.

#include <utility> 
#include <iterator> 
#include <vector> 
#include <iostream> 

namespace itpair { 
    template <typename T> 
    struct pair_iterator : std::iterator<std::forward_iterator_tag, T> { 
     std::pair<T,T> *container; 
     int idx; 
     pair_iterator(std::pair<T,T> *container, int idx) : container(container), idx(idx) {} 
     T &operator*() const { 
      return idx ? container->second : container->first; 
     } 
     T *operator->() const { 
      return &*this; 
     } 
     friend pair_iterator &operator++(pair_iterator &self) { 
      self.idx += 1; 
      return self; 
     } 
     friend pair_iterator operator++(pair_iterator &self, int) { 
      pair_iterator result = self; 
      ++self; 
      return result; 
     } 
     friend bool operator==(const pair_iterator &self, const pair_iterator &other) { 
      return self.container == other.container && self.idx == other.idx; 
     } 
     friend bool operator!=(const pair_iterator &self, const pair_iterator &other) { 
      return !(self == other); 
     } 
    }; 

    template <typename T> 
    pair_iterator<T> begin(std::pair<T,T> &p) { 
     return pair_iterator<T>(&p, 0); 
    } 
    template <typename T> 
    pair_iterator<T> end(std::pair<T,T> &p) { 
     return pair_iterator<T>(&p, 2); 
    } 
} 

int main() { 
    std::pair<int,int> p = std::make_pair(1, 2); 
    using namespace itpair; 
    std::vector<int> v(begin(p), end(p)); 
    std::cout << v[0] << " " << v[1] << "\n"; 
} 

Tất nhiên bạn cũng muốn có quyền truy cập ngẫu nhiên (có nghĩa là nhiều nhà khai thác hơn).

Giống như mọi người nói, tuy nhiên, đó không thực sự là những gì pair là dành cho. Nó không chỉ là một Container.

0

Tôi đã đưa ra giải pháp này. Không phải là "sexy", nhưng nó sẽ hoạt động:

#include <type_traits> 
#include <iterator> 
#include <utility> 

#include <boost/optional.hpp> 

namespace pair_iterator { 

template <class A, class B, class Pair> 
class PairIterator { 
public: 
    using iterator_category = std::random_access_iterator_tag; 
    using value_type = std::common_type_t<A, B>; 
    using difference_type = std::ptrdiff_t; 
    using pointer = std::add_pointer_t<value_type>; 
    using reference = std::add_lvalue_reference_t<value_type>; 
    using const_reference = std::add_lvalue_reference_t<const value_type>; 
private: 
    boost::optional<Pair &> pair = {}; 
    difference_type index = 2; 
public: 
    PairIterator(
     const boost::optional<Pair &> &pair = {}, 
     difference_type index = 2 
    ) : pair(pair), index(index) {} 

    // Iterator 

    PairIterator(PairIterator&&) = default; 
    PairIterator(const PairIterator&) = default; 
    PairIterator &operator =(PairIterator&&) = default; 
    PairIterator &operator =(const PairIterator&) = default; 
    ~PairIterator() = default; 

    void swap(PairIterator &other) { 
     std::swap(pair, other.pair); 
     std::swap(index, other.index); 
    } 

    reference operator *() { 
     return index == 0 ? pair->first : pair->second; 
    } 

    const_reference operator *() const { 
     return index == 0 ? pair->first : pair->second; 
    } 

    PairIterator &operator ++() { 
     ++index; 
     return *this; 
    } 

    // InputIterator 

    bool operator ==(const PairIterator &other) const { 
     return index == other.index; 
    } 

    bool operator !=(const PairIterator &other) const { 
     return index != other.index; 
    } 

    PairIterator operator ++(int) const { 
     return { pair, index+1 }; 
    } 

    // ForwardIterator 

    // BidirectionalIterator 

    PairIterator &operator --() { 
     --index; 
     return *this; 
    } 

    PairIterator operator --(int) const { 
     return { pair, index-1 }; 
    } 

    // RandomAccessIterator 

    PairIterator &operator +=(difference_type n) { 
     index += n; 
     return *this; 
    } 

    PairIterator operator +(difference_type n) const { 
     return { pair, index+n }; 
    } 

    PairIterator &operator -=(difference_type n) { 
     index -= n; 
     return *this; 
    } 

    PairIterator operator -(difference_type n) const { 
     return { pair, index-n }; 
    } 

    difference_type operator -(const PairIterator &other) const { 
     return index - other.index; 
    } 

    reference operator [](difference_type n) { 
     return (index+n) == 0 ? pair->first : pair->second; 
    } 

    const_reference operator [](difference_type n) const { 
     return (index+n) == 0 ? pair->first : pair->second; 
    } 

    bool operator <(const PairIterator &other) const { 
     return index < other.index; 
    } 

    bool operator >(const PairIterator &other) const { 
     return index > other.index; 
    } 

    bool operator <=(const PairIterator &other) const { 
     return index <= other.index; 
    } 

    bool operator >=(const PairIterator &other) const { 
     return index >= other.index; 
    } 
}; 

template <class A, class B> 
auto begin(std::pair<A, B> &pair) -> 
PairIterator<A, B, std::pair<A, B>> { 
    return { pair, 0 }; 
} 

template <class A, class B> 
auto end(std::pair<A, B> &pair) -> 
PairIterator<A, B, std::pair<A, B>> { 
    return { pair, 2 }; 
} 

template <class A, class B> 
auto begin(const std::pair<A, B> &pair) -> 
PairIterator<const A, const B, const std::pair<A, B>> { 
    return { pair, 0 }; 
} 

template <class A, class B> 
auto end(const std::pair<A, B> &pair) -> 
PairIterator<const A, const B, const std::pair<A, B>> { 
    return { pair, 2 }; 
} 

} // namespace pair_iterator 

namespace std { 

using pair_iterator::begin; 
using pair_iterator::end; 

} // namespace std 
Các vấn đề liên quan