2009-04-16 6 views
7

에서 const를 벡터 멤버 변수에 범위 뭔가 같은 :합치 C++ 반복자는 내가 여기의 조각을 제공하는 클래스 X를 가지고 건설 시간

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

이러한 생성자는 두 개의 범위 [begin1, END1) 및 [begin2을 사슬로 잇다 것, END2) mVec에. 도전은이 나는 가능하면 불필요한 복사를 방지하고자하는 X.

2)의 다른 방법을 통해 일정하게 고려 될 수 있도록 나는 mVec에 const를 보존하려는)

1

이다. 즉, 인서트 (2)의 범위, 한 용액 1 범위 임시 비 CONST을 구성하는 정적 메소드를 가질된다 및 리턴 다음에 복사

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

로하지만 모든 값을 연접 생성자를 정의 적어도 한 여분의 시간, 나는 믿습니다.

답변

9

좋은 문제. 두 범위를 단일 범위로 바꾸는 특정 반복기 래퍼 유형을 구현하려고합니다. 의 라인에 뭔가 :

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

는 이제 사용할 수 있습니다

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

또는 당신이 당신의 생성자를 재 선언 할 필요가 없습니다 (난 그냥 생각했다). 호출자가 도우미 함수를 사용하도록합니다.

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

코드를 확인하지 않았습니다. 머리 꼭대기에서 코드를 입력했습니다. 컴파일 할 수도 있고 할 수도없고, 작동시킬 수도 있고하지 않을 수도 있습니다 ... 그러나 이것을 시작점으로 삼을 수 있습니다.

+0

영리한 솔루션 +1 ... 반복자의 두 범위에 걸쳐있는 반복자 ... nice – veefu

+0

boost :: iterator_facade를 사용하여 작성하는 것이 더 쉽지만이 경우에는 아마도 가장 좋은 방법 일 것입니다. 적어도 우리가 이동 지원을받을 때까지. – Macke

+0

concat_end가 필요하지 않습니다. 생성 된 반복자를 값이 e2 인 T2 유형과 비교하여 bool을 생성 할 수 있습니다. –

2

const을 떨어 뜨리는 것이 가장 좋습니다 (왜 어쨌든 주장하겠습니까?).

그렇지 않으면 연결 반복기를 작성해야합니다. 꽤 많은 코드입니다. 자세한 내용은 this thread을 참조하십시오.

+0

내 경우에는 벡터 멤버 변수가 인스턴스가 생성 된 후에 변경되어서는 안됩니다. 그것을 const로 만드는 것은 컴파일러가 저를 보장하도록 도와줍니다. – SCFrench

+0

연결을 수행하는 데 필요한 코드의 양을 감안할 때 const를 유지하면 코드에 버그가 발생할 가능성이 더 큽니다. – avakar

+0

SCFrench, X :: mvec가 X가 생성 된 후에 변경되지 않을만큼 안전하지 않습니까? – veefu

2

C++의 최고 또는 최악의 기능 중 하나는 사용자의 관점에 따라 필요한 작업을 수행 할 때 악용 할 수 있다는 것입니다.

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

내가 이걸 컴파일하려고하지 않았다 잘못 세부 사항의 일부가있을 수 있습니다 :이 경우에 const_cast 피해자입니다. 그러나 그것은 당신에게 아이디어를 줄 것입니다.

+0

두 범위를 취할 수있는 다소 복잡한 반복자 래퍼를 작업 중입니다 ...이 경우에도 훨씬 간단합니다 ... const_cast 아야! –

+0

이것은 실제로 정의되지 않은 동작입니다. const로 정의 된 객체에서 const를 제거해서는 안됩니다. Const가 아닌 객체에 바인딩 된 const 참조에서 const를 제거 할 수 있습니다. – avakar

+1

정의되지 않은 것이 항상 예측할 수없는 것은 아닙니다. 내가 말했듯이, 그것은 분명히 학대입니다. –

1

정적 메서드는 컴파일러가 수행하는 최적화에 따라 생각만큼 나쁘지 않을 수 있습니다. 그리고 C++ 0x에서 이동 생성자는 현재 일어나고있는 복사를 제거합니다.

한편 래퍼 반복기로 이동하십시오. 코드는 input iterator 만 구현하면되므로 avakar 링크와 마찬가지로 나쁜 코드는 아닙니다.

1

1) mVec에서 const를 보존하여 X의 다른 메소드에서 일정하다고 생각합니다.

  • 이 멤버 변수 const의 호기심을 사용하는 것이다. 그리고 그것은 좋은 디자인을 무시합니다. 정의에 따르면, 구성은 객체를 변경해야하는 프로세스입니다.

  • 개체를 수정할 수 없도록 유지해야한다는 요구 사항에 따라 적절한 캡슐화를 사용하십시오. 클래스의 클라이언트에 대해 mVec을 기반으로 기능을 표시하려면 const - 멤버 함수를 사용해야합니다.

2) 가능하면 불필요한 사본을 피하고 싶습니다. 즉, 하나의 해결책은 범위 1에 임시가 아닌 값을 생성하고, 범위 2를 삽입하여이를 반환하고, 연결 생성자를 정의하는 정적 메서드를 갖는 것입니다.

이동 생성자와 r 일반적으로 - 값 참조 (C++ 0x의 약속 된 목표). 이 article을 읽으십시오.

+0

첫 번째 글 머리 기호를 이해하지 못합니다. 내 경우에는 일단 객체가 생성되면 벡터는 수정되지 않은 채로 남아 있어야합니다. 그것을 표시하는 것이 도움이되도록 const를 표시하지 않겠습니까? 멤버 변수에서 const를 사용하는 것이 무엇이겠습니까? – SCFrench

+0

정적이 아닌데도'const' 멤버 변수를 많이 사용하지 않았습니다. 당신이 원하는 것은 읽기 전용 멤버이며, C++에서 원하는대로 할 수있는 구조가 없습니다. 가장 좋은 방법은 const_casts로 인해 UB를 수정하여 const 접근자를 사용하는 것입니다. – dirkgently