"서명 된 / 서명되지 않은 불일치"경고 (C4018)를 어떻게 처리합니까?


80

저는 고성능과 낮은 메모리 오버 헤드를 염두에두고 C ++로 작성된 많은 계산 코드로 작업합니다. STL 컨테이너 (대부분 vector)를 많이 사용하며 거의 모든 단일 함수에서 해당 컨테이너를 반복합니다.

반복 코드는 다음과 같습니다.

for (int i = 0; i < things.size(); ++i)
{
    // ...
}

그러나 서명 된 / 서명되지 않은 불일치 경고 (Visual Studio의 C4018)를 생성합니다.

교체 int일부 unsigned유형 우리가 자주의 OpenMP 프라그 마를 사용하기 때문에 문제이며, 그것은으로 카운터가 필요합니다 int.

나는 (수백 개의) 경고를 억제하려고하는데 문제에 대한 우아한 해결책을 놓친 것 같아 두렵다.

반복자에서 . 반복자는 적절한 장소에 적용될 때 훌륭하다고 생각합니다. 내가 작업중인 코드는 임의 액세스 컨테이너를 또는 다른 것으로 변경 하지 않으며list (따라서 반복 int i은 이미 컨테이너에 구애받지 않습니다) 항상 현재 인덱스가 필요합니다. 그리고 입력해야하는 모든 추가 코드 (반복자 자체 및 인덱스)는 문제를 복잡하게 만들고 기본 코드의 단순성을 난독하게 만듭니다.


1
OpenMP pragma가 서명되지 않은 유형을 사용하지 못하게하는 예를 게시 할 수 있습니까? 에 따르면 가 아니라, 어떤 intergal 유형에 대해 작동합니다 int.
Billy ONeal 2011

4
나는이 질문이 stackoverflow에 더 좋다고 생각합니다.
bcsanches 2011

1
int그리고 std::vector<T>::size_type또한 크기뿐만 아니라 부호의 다를 수 있습니다. 예를 들어, LLP64 시스템 (64 비트와 같은 윈도우)에 sizeof(int) == 4있지만 sizeof(std::vector<T>::size_type) == 8.
Adrian McCarthy


답변:


60

그것은 모두 당신의 things.size()유형입니다. 는 int아니지만 size_t(C 가 아닌 C ++에 존재) 일부 "일반적인"부호없는 유형, 즉 unsigned intx86_32에 해당합니다.

연산자 "less"(<)는 부호가 다른 두 피연산자에 적용될 수 없습니다. 그러한 opcode는 없으며 표준은 컴파일러가 암시 적 부호 변환을 수행 할 수 있는지 여부를 지정하지 않습니다. 따라서 부호있는 숫자를 부호없는 것으로 취급하고 경고를 내 보냅니다.

다음과 같이 쓰는 것이 맞을 것입니다.

for (size_t i = 0; i < things.size(); ++i) { /**/ }

또는 더 빠르게

for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }

17
-1 아니요, 아닙니다 size_t. 그것입니다 std::vector< THING >::size_type.
Raedwald 2011 년

8
@Raedwald : 기술적으로는 정확하지만 표준을 준수하는 구현이 std::size_t및에 대한 다른 기본 유형으로 어떻게 끝날 수 있는지 상상하기 어렵습니다 std::vector<T>::size_type.
Adrian McCarthy

4
++ i가 더 나은 것으로 간주되는 이유는 무엇입니까? for 루프에서 차이가 없습니까?
Shoaib

2
@ShoaibHaider, 반환 값이 사용되지 않는 기본 요소에는 전혀 문제가되지 않습니다. 그러나 사용자 정의 유형 (연산자가 오버로드 된 경우)의 경우 사후 증가는 거의 항상 효율성이 떨어집니다 (증가되기 전에 객체의 복사본을 만들어야하므로). 컴파일러는 사용자 지정 형식에 대해 (필수적으로) 최적화 할 수 없습니다. 따라서 유일한 장점은 일관성입니다 (기본 유형 대 사용자 정의 유형).
Kat

2
@zenith : 네, 맞아요. 내 진술은 기본 할당 자에만 적용됩니다. 사용자 지정 할당자는 std :: size_t 이외의 것을 사용할 수 있지만 여전히 부호없는 정수 유형이어야하며 std :: size_t보다 더 큰 범위를 나타낼 수 없으므로 std를 사용하는 것이 안전합니다. :: size_t는 루프 인덱스의 유형입니다.
Adrian McCarthy

13

이상적으로는 다음과 같은 구조를 대신 사용합니다.

for (std::vector<your_type>::const_iterator i = things.begin(); i != things.end(); ++i)
{
  // if you ever need the distance, you may call std::distance
  // it won't cause any overhead because the compiler will likely optimize the call
  size_t distance = std::distance(things.begin(), i);
}

이것은 코드가 갑자기 컨테이너에 구애받지 않는 깔끔한 이점이 있습니다.

그리고 문제와 관련하여 사용하는 일부 라이브러리 가 더 적합한 int곳 에서 사용해야 하는 경우 unsigned intAPI가 지저분합니다. 어쨌든, 그것들 int이 항상 긍정적 이라고 확신한다면 다음과 같이 할 수 있습니다.

int int_distance = static_cast<int>(distance);

컴파일러에 대한 의도를 명확하게 지정합니다. 더 이상 경고로 인해 버그가 발생하지 않습니다.


1
나는 항상 거리가 필요합니다. static_cast<int>(things.size())다른 사람이 없다면 아마도 해결책이 될 수 있습니다.
Andrew T

@Andrew : 경고를 표시하지 않기로 결정한 경우 가장 좋은 방법은 #pragma warning(push) #pragma warning(disable: 4018) /* ... function */ #pragma warning(pop)불필요한 캐스트를 사용하는 대신 컴파일러 특정 pragma (MSVC에서는 )를 사용하는 것입니다. (캐스트는 합법적 인 오류를 숨 깁니다, m'kay?;))
Billy ONeal 2011

사실이 아니다. 캐스트! = 변환. 경고는 안전하지 않을 수있는 표준에서 허용하는 암시 적 변환에 대한 경고입니다. 그러나 캐스트는 원래 경고에서 말한 것보다 더 안전하지 않을 수있는 명백한 전환을 파티에 초대합니다. 엄밀히 말하면, 적어도 x86에서는 변환이 전혀 일어나지 않을 것입니다. 프로세서는 올바른 명령을 사용하여 작업하는 한 메모리의 특정 부분을 서명 또는 서명되지 않은 것으로 처리하는지 여부를 신경 쓰지 않습니다.
Billy ONeal 2011

컨테이너에 구애받지 않고 O (N ^ 2) 복잡성이 발생하면 (그리고 귀하의 예에서는 list <>에 대해 distance ()가 O (N)이므로) 이점이 있는지 잘 모르겠습니다 :-(.
무 버그 토끼

@ No-BugsHare 그게 바로 요점입니다 : 우리는 확신 할 수 없습니다. OP에 요소가 거의 없다면 아마도 훌륭 할 것입니다. 그가 수백만 개를 가지고 있다면 아마 그다지 많지 않을 것입니다. 프로파일 링 만이 결국 알 수 있지만 좋은 소식은 항상 유지 관리 가능한 코드를 최적화 할 수 있다는 것입니다!
ereOn

9

당신은 / 반복자를 사용하지 않습니다 당신은 / 사용하지 않을 수없는 경우 수없는 경우 std::size_t루프 인덱스를 들어,을 .size()int문서 가정이와 변환이 명시 적으로 컴파일러 경고를 침묵 않는 변환 기능.

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

// When using int loop indexes, use size_as_int(container) instead of
// container.size() in order to document the inherent assumption that the size
// of the container can be represented by an int.
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(things); ++i) { ... }

이 함수 템플릿의 인스턴스화는 거의 확실하게 인라인됩니다. 디버그 빌드에서는 가정이 확인됩니다. 릴리스 빌드에서는 그렇지 않으며 size ()를 직접 호출 한 것처럼 코드가 빠릅니다. 두 버전 모두 컴파일러 경고를 생성하지 않으며 관용적 루프에 대한 약간의 수정일뿐입니다.

릴리스 버전에서도 가정 실패를 포착하려면 어설 션을 다음과 같은 if 문으로 대체 할 수 있습니다 std::out_of_range("container size exceeds range of int").

이렇게하면 부호있는 / 부호없는 비교와 잠재적 인 sizeof(int)! = sizeof(Container::size_type)문제가 모두 해결됩니다. 모든 경고를 활성화 된 상태로두고이를 사용하여 코드의 다른 부분에서 실제 버그를 포착 할 수 있습니다.


6

당신이 사용할 수있는:

  1. size_t 유형, 경고 메시지 제거
  2. 반복자 + 거리 (예 : 첫 번째 힌트)
  3. 반복자 만
  4. 함수 객체

예를 들면 :

// simple class who output his value
class ConsoleOutput
{
public:
  ConsoleOutput(int value):m_value(value) { }
  int Value() const { return m_value; }
private:
  int m_value;
};

// functional object
class Predicat
{
public:
  void operator()(ConsoleOutput const& item)
  {
    std::cout << item.Value() << std::endl;
  }
};

void main()
{
  // fill list
  std::vector<ConsoleOutput> list;
  list.push_back(ConsoleOutput(1));
  list.push_back(ConsoleOutput(8));

  // 1) using size_t
  for (size_t i = 0; i < list.size(); ++i)
  {
    std::cout << list.at(i).Value() << std::endl;
  }

  // 2) iterators + distance, for std::distance only non const iterators
  std::vector<ConsoleOutput>::iterator itDistance = list.begin(), endDistance = list.end();
  for ( ; itDistance != endDistance; ++itDistance)
  {
    // int or size_t
    int const position = static_cast<int>(std::distance(list.begin(), itDistance));
    std::cout << list.at(position).Value() << std::endl;
  }

  // 3) iterators
  std::vector<ConsoleOutput>::const_iterator it = list.begin(), end = list.end();
  for ( ; it != end; ++it)
  {
    std::cout << (*it).Value() << std::endl;
  }
  // 4) functional objects
  std::for_each(list.begin(), list.end(), Predicat());
}

3

C ++ 11에 대한 다음 솔루션을 제안 할 수도 있습니다.

for (auto p = 0U; p < sys.size(); p++) {

}

(C ++는 auto p = 0에 대해 충분히 똑똑하지 않으므로 p = 0U를 넣어야합니다 ....)


1
C ++ 11의 경우 +1. C ++ 11을 사용할 수없는 타당한 이유 가 없다면 새로운 기능을 사용하는 것이 최선이라고 생각합니다. 이는 큰 도움이 될 것입니다. 그리고 for (auto thing : vector_of_things)실제로 색인이 필요하지 않은 경우 확실히 사용 하십시오.
parker.sikand

그러나 그것은 서명 문제만을 해결합니다. size()unsigned int보다 큰 형식을 반환하는 경우에는 도움이되지 않습니다 . 이는 매우 일반적입니다.
Adrian McCarthy

3

더 나은 아이디어를 드릴게요

for(decltype(things.size()) i = 0; i < things.size(); i++){
                   //...
}

decltype 이다

선언 된 엔터티 유형 또는 식의 유형 및 값 범주를 검사합니다.

따라서의 유형을 추론 things.size()하고와 i같은 유형이됩니다 things.size(). 따라서 i < things.size()경고없이 실행됩니다.


0

비슷한 문제가있었습니다. size_t 사용이 작동하지 않았습니다. 나는 나를 위해 일한 다른 것을 시도했습니다. (아래)

for(int i = things.size()-1;i>=0;i--)
{
 //...
}

0

난 그냥 할거야

int pnSize = primeNumber.size();
for (int i = 0; i < pnSize; i++)
    cout << primeNumber[i] << ' ';
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.