문제

나는 프로젝트에서 제가 서명 된 정수를 작업하고 있다고 확신합니다. 내부에 포함 된 가치는 결코 부정적 일 수는 없지만 대부분의 경우에 최선의 선택이라고 확신합니다. (루프에 대한 더 간단한 역전, 버그 가능성 등, 특히 0과 예를 들어 20, 20 사이의 값 만 보유 할 수있는 정수의 경우).

이것이 잘못된 곳의 대부분의 장소는 STD :: 벡터의 간단한 반복입니다. 종종 이것은 과거의 배열이었으며 나중에 STD :: 벡터로 변경되었습니다. 따라서이 루프는 일반적으로 다음과 같습니다.

for (int i = 0; i < someVector.size(); ++i) { /* do stuff */ }

이 패턴은 자주 사용되기 때문에 서명 된 유형과 부호없는 유형의 비교에 대한 컴파일러 경고 스팸의 양은 더 유용한 경고를 숨기는 경향이 있습니다. int_max 요소보다 더 많은 벡터가 없으며 지금까지 컴파일러 경고를 수정하기 위해 두 가지 방법을 사용했습니다.

for (unsigned i = 0; i < someVector.size(); ++i) { /*do stuff*/ }

이것은 일반적으로 작동하지만 루프에 'if (i-1> = 0) 등과 같은 코드가 포함되어 있으면 조용히 깨질 수 있습니다.

for (int i = 0; i < static_cast<int>(someVector.size()); ++i) { /*do stuff*/ }

이 변경에는 부작용이 없지만 루프를 훨씬 덜 읽기 쉽게 만듭니다. (그리고 더 타이핑합니다.)

그래서 나는 다음과 같은 아이디어를 생각해 냈습니다.

template <typename T> struct vector : public std::vector<T>
{
    typedef std::vector<T> base;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }
    int capacity() const { return base::capacity(); }

    vector()                  : base() {}
    vector(int n)             : base(n) {}
    vector(int n, const T& t) : base(n, t) {}
    vector(const base& other) : base(other) {}
};

template <typename Key, typename Data> struct map : public std::map<Key, Data>
{
    typedef std::map<Key, Data> base;
    typedef typename base::key_compare key_compare;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }

    int erase(const Key& k) { return base::erase(k); }
    int count(const Key& k) { return base::count(k); }

    map() : base() {}
    map(const key_compare& comp) : base(comp) {}
    template <class InputIterator> map(InputIterator f, InputIterator l) : base(f, l) {}
    template <class InputIterator> map(InputIterator f, InputIterator l, const key_compare& comp) : base(f, l, comp) {}
    map(const base& other) : base(other) {}
};

// TODO: similar code for other container types

당신이 보는 것은 기본적으로 size_type를 재정의하는 메소드가있는 STL 클래스입니다. 생성자는 상속되지 않기 때문에 필요합니다.

기존 코드베이스에서 이와 같은 솔루션이 보이면 개발자로 어떻게 생각하십니까?

'WHAA, 그들은 STL을 재정의하고 있는데, 얼마나 큰 WTF!' '라고 생각하거나 버그를 방지하고 가독성을 높이기위한 좋은 간단한 솔루션이라고 생각 하시겠습니까? 아니면 STD :: vector <> :: iterator를 사용하기 위해이 모든 루프를 변경하는 데 하루 정도 (반) 하루를 보냈던 것을 볼 수 있습니까?

(특히이 솔루션이 원시 데이터 (예 : 서명되지 않은 Char) 및 비트 마스크를 제외한 서명되지 않은 유형의 사용을 금지하는 것과 결합 된 경우).

도움이 되었습니까?

해결책

이 커뮤니티 위키를 만들었습니다 ... 편집 해주세요. 더 이상 "int"에 대한 조언에 동의하지 않습니다. 나는 이제 그것을 나쁘지 않은 것으로 본다.

예, 리차드에 동의합니다. 당신은 절대 사용해서는 안됩니다 'int' 루프에서 카운팅 변수로. 다음은 인덱스를 사용하여 다양한 루프를 수행하는 방법입니다 (이유가 거의 없으며 때로는 유용 할 수 있습니다).

앞으로

for(std::vector<int>::size_type i = 0; i < someVector.size(); i++) {
    /* ... */
}

뒤로

완벽하게 정의 된 Behaivor :이 작업을 수행 할 수 있습니다.

for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* ... */
}

곧 C ++ 1X (다음 C ++ 버전)가 멋지게 나오면 다음과 같이 할 수 있습니다.

for(auto i = someVector.size() - 1; i != (decltype(i)) -1; i--) {
    /* ... */
}

0 이하로 줄이면 서명되지 않기 때문에 주위를 감싸게됩니다.

그러나 서명되지 않은 것은 버그가 흘러 나올 것입니다

그것은 잘못된 길로 만들기위한 논쟁이되어서는 안됩니다 (사용 'int').

위의 std :: size_t를 사용하지 않는 이유는 무엇입니까?

C ++ 표준은 IN을 정의합니다 23.1 p5 Container Requirements, 저것 T::size_type , 을 위한 T 일부입니다 Container, 이 유형은 서명되지 않은 정의 된 정의 된 일부 구현입니다. 이제 사용합니다 std::size_t ~을 위한 i 위의 버그는 버그가 조용히 흘러 나옵니다. 만약에 T::size_type 보다 적습니다 std::size_t, 그러면 오버플로됩니다 i, 심지어 일어나지 않습니다 (std::size_t)-1 만약에 someVector.size() == 0. 마찬가지로, 루프의 상태는 완전히 고장 났을 것입니다.

다른 팁

STL 컨테이너에서 공개적으로 파생하지 마십시오. 누구든지 포인터-베이스를 통해 객체 중 하나를 삭제하면 정의되지 않은 동작을 불러 일으키는 비 침전소가 있습니다. 예를 들어 벡터에서 예를 들어, 개인적으로 수행하고 노출 해야하는 부품을 노출시켜야합니다. using 선언.

여기, 나는 단지 a를 사용합니다 size_t 루프 변수로 간단하고 읽을 수 있습니다. 이를 사용한 포스터 int INDEX는 N00B가 정확하므로 귀하를 노출시킵니다. 그러나 반복자를 사용하여 벡터를 통해 루프를 사용하면 약간 더 숙련 된 N00B로 노출됩니다. (vector<T>::size_type 정확하지만 불필요하게 장황한 IMO).

"반복기 사용, 그렇지 않으면 N00B를 보는 것"은 문제에 대한 좋은 해결책이라고 생각하지 않지만 std :: 벡터에서 파생되는 것은 그보다 훨씬 나빠 보입니다.

먼저, 개발자는 벡터가 std : .vector가 될 것으로 예상하고 MAP는 std :: map으로 기대합니다. 둘째, 솔루션은 다른 컨테이너 또는 컨테이너와 상호 작용하는 다른 클래스/라이브러리의 경우에도 스케일링되지 않습니다.

그렇습니다. 반복자는 못 생겼으며 반복 루프는 잘 읽을 수 없으며 TypEdef는 혼란 만 덮습니다. 그러나 적어도 그들은 규모가 크며 표준 솔루션입니다.

내 해결책? STL-FOR-EACH 매크로. 그것은 문제가없는 것이 아니지만 (주로, 그것은 매크로, yuck) 의미를 가로 지르는 것입니다. 그것은 예를 들어 발전되지 않습니다 이 하나,하지만 일을합니다.

반복기를 확실히 사용하십시오. 곧 다음과 같이 더 나은 가독성 (우려 중 하나)을 위해 '자동'유형을 사용할 수 있습니다.

for (auto i = someVector.begin();
     i != someVector.end();
     ++i)

인덱스를 건너 뛰십시오

가장 쉬운 방법은 반복자, 루프의 범위 기반 또는 알고리즘을 사용하여 문제를 회피하는 것입니다.

for (auto it = begin(v); it != end(v); ++it) { ... }
for (const auto &x : v) { ... }
std::for_each(v.begin(), v.end(), ...);

실제로 인덱스 값이 필요하지 않은 경우 좋은 솔루션입니다. 또한 리버스 루프를 쉽게 처리합니다.

적절한 서명되지 않은 유형을 사용하십시오

또 다른 방법은 컨테이너의 크기 유형을 사용하는 것입니다.

for (std::vector<T>::size_type i = 0; i < v.size(); ++i) { ... }

당신은 또한 사용할 수 있습니다 std::size_t (에서u003Ccstddef> ). (정확하게) std::size_t 같은 유형이 아닐 수 있습니다 std::vector<T>::size_type (보통이지만). 그러나 컨테이너의 size_type a std::size_t. 리버스 루프에 특정 스타일을 사용하지 않는 한 모든 것이 정상입니다. 리버스 루프에 선호하는 스타일은 다음과 같습니다.

for (std::size_t i = v.size(); i-- > 0; ) { ... }

이 스타일로 안전하게 사용할 수 있습니다 std::size_t, 그것이 더 큰 유형이더라도 std::vector<T>::size_type. 다른 답변 중 일부에 표시된 리버스 루프 스타일은 -1을 올바른 유형으로 캐스팅해야하므로 유형이 더 쉬운 것을 사용할 수 없습니다. std::size_t.

서명 된 유형을 사용하십시오 (조심스럽게!)

서명 된 유형을 정말로 사용하려는 경우 (또는 스타일 가이드는 실제로 하나를 요구합니다), 처럼 int, 그러면 디버그 빌드에서 기본 가정을 확인하고 컴파일러 경고 메시지를받지 못하도록 변환을 명시하게 만드는이 작은 함수 템플릿을 사용할 수 있습니다.

#include <cassert>
#include <cstddef>
#include <limits>

template <typename ContainerType>
constexpr int size_as_int(const ContainerType &c) {
    const auto size = c.size();  // if no auto, use `typename ContainerType::size_type`
    assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
    return static_cast<int>(size);
}

이제 쓸 수 있습니다.

for (int i = 0; i < size_as_int(v); ++i) { ... }

또는 전통적인 방식으로 리버스 루프 :

for (int i = size_as_int(v) - 1; i >= 0; --i) { ... }

그만큼 size_as_int 트릭은 암시 적 변환이있는 루프보다 약간 더 타이핑되며, 런타임에 근본적인 가정을 확인하고, 명시 적 캐스트와 함께 컴파일러 경고를 침묵시키고, 비 데그 빌드와 동일한 속도를 얻을 수 있기 때문에 거의 확실히 감소되기 때문입니다. 템플릿이 컴파일러가 아직 암시 적으로 수행하지 않았기 때문에 최적화 된 객체 코드는 더 크지 않아야합니다.

당신은 문제를 지나치게 생각하고 있습니다.

size_t 변수를 사용하는 것이 바람직하지만 프로그래머가 서명되지 않은 것을 올바르게 사용한다고 믿지 않는다면 캐스트를 사용하여 추악함을 처리하십시오. 인턴이 그들 모두를 바꾸고 그 후에 걱정하지 마십시오. 오류가 발생하지 않고 새로운 경고가 발생하지 않을 것입니다. 루프는 이제 "못생긴"일 수 있지만, 서명 대 비인지적으로 종교적 입장의 결과로 이해할 수 있습니다.

vector.size() 반환 a size_t var, 그냥 바꾸십시오 int 에게 size_t 그리고 괜찮을 것입니다.

Richard의 대답은 간단한 루프를위한 많은 작업이라는 점을 제외하고는 더 정확합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top