2009-04-16 28 views
7

Tôi có một lớp X, mà tôi cung cấp một đoạn ở đây:concatenating C++ iterator dao vào một biến thành viên vector const lúc xây dựng

class X { 
    public: 
    template <typename Iter> 
    X(Iter begin, Iter end) : mVec(begin, end) {} 

    private: 
    vector<Y> const mVec; 
}; 

bây giờ tôi muốn thêm một constructor concatenating mới vào lớp này, một cái gì đó như:

template <typename Iter1, typename Iter2> 
X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) : mVec(???) { ??? } 

Nhà xây dựng như vậy sẽ bắt chước hai dãy [begin1, end1) và [begin2, end2) vào mVec. Những thách thức là

1) Tôi muốn giữ gìn const trên mVec, do đó nó được coi là không đổi trong suốt các phương pháp khác của X.

2) Tôi muốn tránh bản sao không cần thiết nếu có thể. Đó là, một giải pháp là phải có một phương pháp tĩnh mà xây dựng một tổ chức phi const tạm thời để khoảng 1, chèn khoảng 2 và trả về nó, và sau đó xác định các nhà xây dựng concatenating để

template <typename Iter1, typename Iter2> 
X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) 
    : mVec(concatenate(begin1, end1, begin2, end2)) { } 

nhưng điều đó copy tất cả các giá trị tại ít nhất một lần nữa, tôi tin.

Trả lời

9

Vấn đề hay. Tôi sẽ cố gắng để thực hiện một loại trình bao bọc lặp cụ thể mà biến hai phạm vi thành một phạm vi duy nhất. Một cái gì đó trong dòng:

// compacted syntax for brevity... 
template <typename T1, typename T2> 
struct concat_iterator 
{ 
public: 
    typedef std::forward_iterator_tag iterator_category; 
    typedef typename iterator_traits<T1>::value_type value_type; 
    typedef *value_type pointer; 
    typedef &value_type reference; 

    concat_iterator(T1 b1, T1 e1, T2 b2, T2 e2) 
     : seq1(b1), seq1end(e1), seq2(b2), seq2end(e2); 
    iterator& operator++() { 
     if (seq1 != seq1end) ++seq1; 
     else ++seq2; 
     return this; 
    } 
    reference operator*() { 
     if (seq1 != seq1end) return *seq1; 
     else return *seq2; 
    } 
    pointer operator->() { 
     if (seq1 != seq1end) return &(*seq1); 
     else return &(*seq2); 
    } 
    bool operator==(concat_iterator const & rhs) { 
     return seq1==rhs.seq1 && seq1end==rhs.seq2 
      && seq2==rhs.seq2 && seq2end==rhs.seq2end; 
    } 
    bool operator!=(contact_iterator const & rhs) { 
     return !(*this == rhs); 
    } 
private: 
    T1 seq1; 
    T1 seq1end; 
    T2 seq2; 
    T2 seq2end; 
}; 

template <typename T1, typename T2> 
concat_iterator<T1,T2> concat_begin(T1 b1, T1 e1, T2 b2, T2 e2) 
{ 
    return concat_iterator<T1,T2>(b1,e1,b2,e2); 
} 
template <typename T1, typename T2> 
concat_iterator<T1,T2> concat_end(T1 b1, T1 e1, T2 b2, T2 e2) 
{ 
    return concat_iterator<T1,T2>(e1,e1,e2,e2); 
} 

Bây giờ bạn có thể sử dụng:

class X { 
public: 
    template <typename Iter, typename Iter2> 
    X(Iter b1, Iter e1, Iter2 b2, Iter2 e2) 
     : mVec(concat_begin(b1,e1,b2,e2), concat_end(b1,e1,b2,e2)) 
    {} 

    private: 
    vector<Y> const mVec; 
}; 

hoặc (Tôi vừa nghĩ về nó), bạn không cần phải redeclare constructor của bạn. Làm cho người gọi của bạn sử dụng các chức năng trợ giúp:

X x(concat_begin(b1,e1,b2,e2), concat_end(b1,e1,b2,e2)); 

Tôi chưa kiểm tra mã, chỉ cần nhập mã ở đây khỏi đầu của tôi. Nó có thể biên dịch hoặc nó có thể không, nó có thể làm việc hay không ... nhưng bạn có thể lấy điều này như là một điểm khởi đầu.

+0

+1 cho giải pháp thông minh ... một vòng lặp trải dài hai dãy vòng lặp ... đẹp – veefu

+0

Nó sẽ dễ viết hơn bằng cách sử dụng boost :: iterator_facade, nhưng đây có lẽ là đặt cược tốt nhất trong trường hợp này. Ít nhất, cho đến khi chúng tôi có hỗ trợ di chuyển. – Macke

+0

Không thấy nhu cầu concat_end. Bạn có thể so sánh trình lặp được tạo với kiểu T2 của giá trị e2 ang sinh ra một bool. –

2

Nó có lẽ là tốt nhất để thả const (tại sao bạn sẽ nhấn mạnh vào nó?).

Nếu không, bạn phải tạo trình lặp lặp lại. Đó là khá nhiều mã, xem this thread để biết thêm.

+0

Trong trường hợp của tôi, biến thành viên vectơ không được thay đổi sau khi cá thể đã được tạo. Làm cho nó const giúp trình biên dịch giúp tôi đảm bảo điều đó. – SCFrench

+0

Vâng, với số lượng mã cần thiết để thực hiện việc nối, có nhiều khả năng mã của bạn sẽ bị lỗi nếu bạn giữ nguyên const. – avakar

+0

SCFrench, không phải là nó đủ an toàn mà X :: mvec sẽ không thay đổi sau khi X đã được xây dựng? – veefu

2

Một trong những tính năng tốt nhất hoặc tệ nhất của C++, tùy thuộc vào quan điểm của bạn, là bạn có thể lạm dụng nó khi cần thiết để hoàn thành công việc. Trong trường hợp này, const_cast là nạn nhân:

template <typename Iter1, typename Iter2> 
X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) : mVec(begin1, end1) { 
    const_cast<vector<Y>&>(mVec).insert(mVec.end(), begin2, end2); 
} 

Tôi có thể có một số chi tiết sai, tôi không cố gắng biên dịch điều này. Nhưng nó sẽ cho bạn ý tưởng.

+0

Tôi đã làm việc trên một wrapper iterator khá phức tạp mà sẽ mất cả hai phạm vi ... điều này là đơn giản hơn nhiều, ngay cả khi ... const_cast ouch! –

+0

Đây thực sự là hành vi không xác định. Bạn không nên xóa const khỏi các đối tượng đã được định nghĩa const. Bạn có thể loại bỏ const từ các tham chiếu const liên kết với một đối tượng không phải là const. – avakar

+1

Không xác định không phải lúc nào cũng có nghĩa là không thể đoán trước. Như tôi đã nói, đó chắc chắn là sự lạm dụng. –

1

Phương pháp tĩnh của bạn có thể không tệ như bạn nghĩ, tùy thuộc vào việc tối ưu hóa trình biên dịch của bạn. Và trong C++ 0x, việc di chuyển các hàm tạo sẽ loại bỏ bất kỳ việc sao chép nào hiện đang diễn ra.

Trong thời gian chờ đợi, hãy sử dụng trình bao bọc trình bao bọc. Mã không có khả năng xấu như các liên kết avakar luồng đến, vì bạn chỉ cần triển khai một input iterator.

1

1) Tôi muốn giữ nguyên const trên mVec, để nó được coi là không đổi trong suốt các phương pháp khác của X.

  • Đây là một sử dụng tò mò của const trên một biến thành viên. Và nó bất chấp thiết kế tốt. Theo định nghĩa, xây dựng là một quá trình đòi hỏi đối tượng phải thay đổi.

  • Đối với yêu cầu của bạn để giữ cho đối tượng không thể sửa đổi - hãy sử dụng đóng gói thích hợp. Bạn nên sử dụng const chức năng dành cho thành viên để hiển thị bất kỳ chức năng nào dựa trên số mVec của bạn cho khách hàng trong lớp học của bạn.

2) Tôi muốn tránh các bản sao không cần thiết nếu có thể. Đó là, một giải pháp là có một phương pháp tĩnh xây dựng một không const tạm thời để phạm vi 1, chèn phạm vi 2 và trả về nó, và sau đó xác định các nhà xây dựng ghép nối để

Bạn nên xem xét di chuyển-nhà xây dựng và r tham khảo giá trị nói chung (mục tiêu đã hứa của C++ 0x). Đọc số article này.

+0

Tôi không hiểu viên đạn đầu tiên của bạn. Trong trường hợp của tôi, một khi đối tượng được tạo ra, vectơ được cho là không được sửa đổi. Sẽ không đánh dấu nó const giúp thực thi điều đó? Điều gì sẽ là một sử dụng không kỳ lạ của const trên một biến thành viên? – SCFrench

+0

Tôi chưa thấy nhiều việc sử dụng biến thành viên 'const' khi nó không tĩnh. Những gì bạn muốn là một thành viên chỉ đọc và không có cấu trúc trong C++ để làm điều đó theo cách bạn muốn. Đặt cược tốt nhất của bạn là sử dụng các trình truy cập const thay vì gọi UB bằng cách sửa đổi do const_casts. – dirkgently

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