C ++의 이중 부정


124

방금 꽤 거대한 코드 기반으로 프로젝트를 시작했습니다.

나는 주로 C ++를 다루고 있으며 그들이 작성하는 많은 코드는 부울 논리에 대해 이중 부정을 사용합니다.

 if (!!variable && (!!api.lookup("some-string"))) {
       do_some_stuff();
 }                                   

저는이 사람들이 똑똑한 프로그래머라는 것을 알고 있습니다. 우연히이 일을하지 않는 것은 분명합니다.

나는 노련한 C ++ 전문가가 아닙니다. 그들이 이것을하는 이유에 대한 유일한 추측은 평가되는 값이 실제 부울 표현이라는 것을 절대적으로 긍정적으로 만들고 싶어한다는 것입니다. 그래서 그들은 그것을 부정한 다음 그것을 다시 부정하여 실제 부울 값으로 되돌립니다.

이것이 맞습니까, 아니면 뭔가 빠졌습니까?


4
여기를 확인하십시오, 이미 물었습니다 . C ++에서 bool로 변환하는 안전한 방법?
Özgür

이 주제는 여기서 논의 되었습니다 .
Dima

답변:


121

bool로 변환하는 것은 트릭입니다.


19
(bool)로 명시 적으로 캐스팅하는 것이 더 명확 할 것이라고 생각합니다. 타이핑이 적기 때문에 왜이 까다로운 !!를 사용합니까?
Baiyan Huang

27
그러나 C ++ 또는 최신 C에서는 의미가 없거나 결과가 부울 식에서 만 사용되는 경우 (질문에서와 같이). bool유형 이 없었을 때 10부울 변수 이외의 값을 저장하는 것을 방지하기 위해 유용했습니다 .
Mike Seymour

6
@lzprgmr : 명시 적 캐스트가 MSVC 에서 "성능 경고" 를 발생시킵니다 . 문제를 사용 !!하거나 !=0해결하고 두 가지 중에서 이전 클리너를 찾습니다 (더 많은 유형에서 작동하기 때문에). 또한 문제의 코드에서 사용할 이유가 없다는 데 동의합니다.
Yakov Galka 2012 년

6
@Noldorin, 나는 그것이 가독성 을 향상 시킨다고 생각합니다 -그것이 의미하는 바를 알고 있다면 간단하고 깔끔하며 논리적입니다.
JWG

19
개선? 빌어 먹을 지옥 ... 당신이 담배를 피우는 걸 좀주세요.
Noldorin 2014-06-04

73

실제로 어떤 상황에서는 매우 유용한 관용구입니다. 이 매크로를 사용하십시오 (Linux 커널의 예). GCC의 경우 다음과 같이 구현됩니다.

#define likely(cond)   (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))

왜 이렇게해야합니까? GCC __builtin_expect는 매개 변수를으로 취급 long하지 않고 으로 취급 bool하므로 어떤 형태의 변환이 필요합니다. cond매크로를 작성할 때가 무엇인지 모르기 때문에 단순히 !!관용구 를 사용하는 것이 가장 일반적 입니다.

0과 비교하여 똑같은 일을 할 수 있지만, 제 생각에는 이중 부정을 수행하는 것이 실제로 더 간단합니다. C가 가진 캐스트-볼에 가장 가깝기 때문입니다.

이 코드는 C ++에서도 사용할 수 있습니다. 가장 낮은 공통 분모입니다. 가능하면 C와 C ++ 모두에서 작동하는 작업을 수행하십시오.


당신이 그것을 생각할 때 이것이 많은 의미가 있다고 생각합니다. 모든 답변을 읽지는 않았지만 변환 프로세스가 지정되지 않은 것 같습니다. 2 비트 높이의 값이 있고 높이가 1 비트 뿐인 값이 있으면 0이 아닌 값을 갖게됩니다. 0이 아닌 값을 부정하면 부울 변환이 발생합니다 (0이면 false, 그렇지 않으면 true). 그런 다음 다시 부정하면 원래 진실을 나타내는 부울이 생성됩니다.
Joey Carson

그래서 내가 내 댓글을 업데이트하는 것을 허용하지 않기 때문에 내 실수에 수정을 추가 할 것입니다. 정수 값을 부정하면 부울 변환이 발생합니다 (0이 아닌 경우 false, 그렇지 않으면 true).
Joey Carson

51

코더는 피연산자를 bool로 변환 할 것이라고 생각하지만 &&의 피연산자는 이미 암시 적으로 bool로 변환되었으므로 완전히 중복됩니다.


14
Visual C ++는 이러한 트릭없이 경우에 따라 성능 저하를 제공합니다.
Kirill V. Lyadvinsky

1
코드에서 쓸모없는 경고를 해결하는 것보다 경고를 비활성화하는 것이 가장 좋습니다.
Ruslan

아마도 그들은 그것을 깨닫지 못할 것입니다. 이것은 당신이 알지 못하는 정수 데이터 유형으로 작업 할 수있는 매크로의 맥락에서 완벽하게 의미가 있습니다. 비트 필드를 나타내는 정수 값을 반환하려면 괄호 연산자가 오버로드 된 개체를 고려하십시오.
Joey Carson

12

쓰기를 피하는 기술입니다 (변수! = 0). 즉, 어떤 유형이든 bool로 변환하는 것입니다.

이와 같은 IMO 코드는 즉시 읽을 수있는 코드가 아니기 때문에 유지 관리해야하는 시스템에 적합하지 않습니다.

코드는 읽을 수 있어야합니다. 그렇지 않으면 불필요하게 복잡한 것을 이해하는 데 시간이 걸리기 때문에 미래를위한 시간 빚을 남깁니다.


8
내 속임수에 대한 정의는 모든 사람이 첫 번째 독서에서 이해할 수없는 것입니다. 알아 내야 할 것은 속임수입니다. 또한 끔찍한! 연산자가 과부하 될 수 있습니다 ...
Richard Harrison

6
@ orlandu63 : 간단한 typecasting은 bool(expr)옳은 일을하고 모두가 첫눈에 그 의도를 이해한다는 것입니다. !!(expr)실수로 bool로 변환되는 이중 부정입니다. 이것은 간단하지 않습니다.
Adrien Plisson

12

예, 맞습니다. 당신은 무언가를 놓치고 있지 않습니다. !!bool 로의 변환입니다. 자세한 내용은 이 질문 을 참조하십시오 .


9

컴파일러 경고를 회피합니다. 이 시도:

int _tmain(int argc, _TCHAR* argv[])
{
    int foo = 5;
    bool bar = foo;
    bool baz = !!foo;
    return 0;
}

'bar'줄은 MSVC ++에서 "부울 'true'또는 'false'(성능 경고) 값을 강제로 생성합니다."를 생성하지만 'baz'줄은 잘 통과합니다.


1
대부분은 일반적으로 알지 못하는 윈도우 API 자체에서 발생 bool유형 - 모든으로 인코딩 0또는 1int.
Mark Ransom

4

운영자입니다! 과부하?
그렇지 않은 경우 경고를 생성하지 않고 변수를 bool로 변환하기 위해이 작업을 수행 할 수 있습니다. 이것은 일을하는 표준 방법이 아닙니다.


4

레거시 C 개발자들은 종종 있으므로, 어떤 부울 유형을 없었다 #define TRUE 1#define FALSE 0다음 부울 비교를 임의의 숫자 데이터 유형을 사용했다. 이제 bool많은 컴파일러가 숫자 유형과 부울 유형의 혼합을 사용하여 특정 유형의 할당 및 비교를 수행 할 때 경고를 내 보냅니다. 이 두 가지 사용법은 결국 레거시 코드로 작업 할 때 충돌합니다.

이 문제를 해결하기 위해 일부 개발자는 다음 부울 ID를 사용합니다. !num_valuereturns bool trueif num_value == 0; false그렇지 않으면. if !!num_value반환 ; 그렇지 않으면. 단일 부정은로 변환 하기에 충분 합니다 . 그러나 Boolean 표현식의 원래 감각을 복원하려면 이중 부정이 필요합니다.bool falsenum_value == 0truenum_valuebool

이 패턴은 관용구 , 즉 언어에 익숙한 사람들이 일반적으로 사용 하는 것으로 알려져 있습니다. 따라서 나는 그것을 반 패턴으로 보지 않는다 static_cast<bool>(num_value). 캐스트는 올바른 결과를 제공 할 수 있지만 일부 컴파일러는 성능 경고를 내보이므로 여전히 해결해야합니다.

이를 해결하는 다른 방법은 다음과 같이 말하는 것 (num_value != FALSE)입니다. 나는 그것도 괜찮지 만, !!num_value대체로 훨씬 덜 장황하고 더 명확 할 수 있으며 두 번째로 볼 때 혼란스럽지 않습니다.


2

!! 부울 유형이없는 원래 C ++에 대처하는 데 사용되었습니다 (C도 마찬가지 임).


예제 문제 :

내부 if(condition)에서는 , 등과 condition같은 유형으로 평가할 필요가 double, int, void*있지만 bool아직 존재하지 않습니다.

클래스가 존재하고 int256(256 비트 정수) 모든 정수 변환 / 캐스트가 오버로드되었다고 가정합니다.

int256 x = foo();
if (x) ...

x"참"인지 0이 아닌지 테스트하려면 정수 if (x)로 변환 한 다음 0이 아닌지 평가합니다 . 일반적인 오버로드는 의 LSbit 만 반환합니다 . 그런 다음 LSbits 만 테스트했습니다 .xint(int) xxif (x)x

그러나 C ++에는 !연산자가 있습니다. 오버로드 !x는 일반적으로의 모든 비트를 평가합니다 x. 따라서 비 반전 논리 if (!!x)로 돌아가려면 사용됩니다.

Ref 이전 버전의 C ++는`if ()`문에서 조건을 평가할 때 클래스의`int` 연산자를 사용 했습니까?


1

으로 마르신가 언급 한 연산자 오버로딩 플레이에있는 경우, 그것은 또한 문제가 있습니다. 그렇지 않으면 C / C ++에서는 다음 중 하나를 수행하는 경우를 제외하고는 중요하지 않습니다.

  • true(또는 C에서 TRUE매크로 와 같은 ) 직접 비교 는 거의 항상 나쁜 생각입니다. 예를 들면 :

    if (api.lookup("some-string") == true) {...}

  • 당신은 단순히 엄격한 0/1 값으로 변환되기를 원합니다. C ++에서 a에 대한 할당 bool은이를 암시 적으로 수행합니다 (암시 적으로로 변환 할 수있는 작업에 대해 bool). C에서 또는 bool이 아닌 변수를 다루는 경우 이것은 내가 본 관용구이지만 나는 (some_variable != 0)다양성을 선호합니다 .

나는 더 큰 부울 표현식의 맥락에서 단순히 일을 복잡하게 만든다고 생각합니다.


1

경우 변수가 객체 유형이다, 그것은있을 수 있습니다! 연산자가 정의되었지만 bool에 대한 캐스트가 없습니다 (또는 의미가 다른 int에 대한 암시 적 캐스트가 더 심합니다.! 연산자를 두 번 호출하면 이상한 경우에도 작동하는 bool로 변환됩니다.


0

정확하지만 C에서는 여기서 의미가 없습니다 .'if '와'&& '는'!! '없이 같은 방식으로 표현을 처리합니다.

C ++에서 이렇게하는 이유는 '&&'가 오버로드 될 수 있기 때문이라고 생각합니다. 그러나 그런 다음 '!'도 가능하므로 및 유형에 대한 코드를 보지 않고는 부울을 얻는 것이 실제로 보장 되지 않습니다 . C ++ 경험이 더 많은 사람이 설명 할 수있을 것입니다. 아마도 그것은 보장이 아닌 심층 방어 수단을 의미 할 것입니다.variableapi.call


if또는에 대한 피연산자로만 사용되는 경우 컴파일러는 값을 동일한 방식으로 처리 &&하지만을 사용 !!하면 일부 컴파일러 if (!!(number & mask))에서 bit triggered = !!(number & mask); if (triggered); 비트 유형이있는 일부 임베디드 컴파일러에서 비트 유형에 예를 들어 256을 할당하면 0이 생성됩니다. 이 없으면 !!명백히 안전한 변환 ( if조건을 변수에 복사 한 다음 분기)은 안전하지 않습니다.
supercat 2014-04-24

0

아마 프로그래머들은 이런 생각을하고 있었을 것입니다 ...

!! myAnswer는 부울입니다. 문맥 상 부울이되어야하는데, 난 그냥 뱅뱅을해서 확인하는 걸 좋아하는데, 옛날 옛적에 나를 물어 뜯는 신비한 버그가 있었는데 뱅뱅, 죽였거든요.


0

이것은 더블 뱅 트릭 의 예일 수 있습니다 . 자세한 내용 은 The Safe Bool Idiom 을 참조하세요. 여기에 기사의 첫 페이지가 요약되어 있습니다.

C ++에는 클래스에 대한 부울 테스트를 제공하는 여러 가지 방법이 있습니다.

분명한 방법은 operator bool변환 연산자입니다.

// operator bool version
  class Testable {
    bool ok_;
  public:
    explicit Testable(bool b=true):ok_(b) {}

    operator bool() const { // use bool conversion operator
      return ok_;
    }
  };

수업을 테스트하고

Testable test;
  if (test) 
    std::cout << "Yes, test is working!\n";
  else 
    std::cout << "No, test is not working!\n";

그러나 opereator bool이 같은 무의미한 작업 할 수 있기 때문에 안전하지 않은 것으로 간주됩니다 test << 1;또는 int i=test.

operator!암시 적 변환이나 과부하 문제를 방지하기 때문에 사용하는 것이 더 안전합니다.

구현은 간단합니다.

bool operator!() const { // use operator!
    return !ok_;
  }

Testable객체 를 테스트하는 두 가지 관용적 방법 은 다음과 같습니다.

  Testable test;
  if (!!test) 
    std::cout << "Yes, test is working!\n";
  if (!test2) {
    std::cout << "No, test2 is not working!\n";

첫 번째 버전 if (!!test)은 일부 사람들이 더블 뱅 트릭 이라고 부르는 것 입니다 .


1
C ++ 11 explicit operator bool부터 다른 정수 유형으로의 암시 적 변환을 방지 하는 데 사용할 수 있습니다.
Arne Vogel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.