C ++, 'if'표현식의 변수 선언


113

여기서 무슨 일이 일어나고 있습니까?

if(int a = Func1())
{
    // Works.
}

if((int a = Func1()))
{
    // Fails to compile.
}

if((int a = Func1())
    && (int b = Func2()))
)
{
    // Do stuff with a and b.
    // This is what I'd really like to be able to do.
}

2003 표준의 섹션 6.4.3은 선택문 조건에 선언 된 변수가 조건에 의해 제어되는 하위 명령문의 끝까지 확장되는 범위를 갖는 방법을 설명합니다. 그러나 나는 그것이 선언 주위에 괄호를 넣을 수 없다는 것에 대한 어떤 것을 말하는지 보지 못하며 조건 당 하나의 선언에 대해서만 언급하지 않습니다.

이 제한은 조건에 하나의 선언 만 필요한 경우에도 성가시다. 이걸 고려하세요.

bool a = false, b = true;

if(bool x = a || b)
{

}

x가 false로 설정된 'if "본문 범위를 입력하려면 선언에 괄호가 필요하지만 (할당 연산자가 논리 OR보다 우선 순위가 낮기 때문에) 괄호를 사용할 수 없기 때문에 외부에 x를 선언해야합니다. 선언문을 원하는 것보다 더 큰 범위로 유출하는 본문입니다. 분명히이 예제는 사소하지만 더 현실적인 경우는 a와 b가 테스트해야하는 값을 반환하는 함수 인 경우입니다.

그래서 내가하고 싶은 것이 표준에 맞지 않는 것입니까, 아니면 컴파일러가 내 공을 파열합니까 (VS2008)?


6
"루프에 들어가려면"<-예제에는 if. if루프가 아니라 조건부입니다.
crashmstr

2
@crashmstr : true,하지만 조건 whileif.
Mike Seymour

2
쉼표 연산자로이 작업을 수행 할 수 없습니까? 내 말은 : if (int a = foo(), int b = bar(), a && b)? 쉼표 연산자가 오버로드되지 않은 경우 표준은 표현식이 왼쪽에서 오른쪽으로 평가되고 결과 값이 마지막 표현식이라고 말합니다. for루프 초기화 와 함께 작동합니다 . 왜 여기에 있지 않습니까?
Archie

@Archie : 방금 이것을 시도했지만 작동하지 못했습니다. 실제 사례를 제공 할 수 있습니까?
James Johnston

@JamesJohnston : 나도 시도했지만 작동하지 않는 것 같습니다. 그 아이디어는 머릿속에서 나왔고 어떻게 if작동 하는지에 대한 제안을 받았는데 잘못된 가정 인 것 같습니다.
Archie

답변:


63

C ++ 17에서 당신이하려는 일 이 마침내 가능합니다 .

if (int a = Func1(), b = Func2(); a && b)
{
    // Do stuff with a and b.
}

선언과 실제 조건을 구분하기 위해 ;대신 사용하는 것에 유의하십시오 ,.


23
좋은! 나는 항상 내가 내 시간보다 앞서 있다고 생각했다.
Neutrino

106

나는 당신이 이미 그 문제를 암시했다고 생각합니다. 컴파일러는이 코드로 무엇을해야합니까?

if (!((1 == 0) && (bool a = false))) {
    // what is "a" initialized to?

"&&"연산자는 단락 논리 AND입니다. 즉, 첫 번째 부분 (1==0)이 거짓으로 판명 (bool a = false)되면 최종 답변이 거짓이라는 것이 이미 알려져 있기 때문에 두 번째 부분 은 평가되지 않아야합니다. 경우 (bool a = false)평가되지 않습니다, 나중에 그 용도에 코드로 무엇을 할 수 a? 변수를 초기화하지 않고 정의되지 않은 상태로 두겠습니까? 기본값으로 초기화할까요? 데이터 유형이 클래스이고 이렇게하면 바람직하지 않은 부작용이 발생하면 어떻게됩니까? bool클래스를 사용하는 대신 사용자 매개 변수 제공 해야하는 기본 생성자가없는 경우 어떻게해야합니까?

다음은 또 다른 예입니다.

class Test {
public:
    // note that no default constructor is provided and user MUST
    // provide some value for parameter "p"
    Test(int p);
}

if (!((1 == 0) && (Test a = Test(5)))) {
    // now what do we do?!  what is "a" set to?

당신이 발견 한 한계가 완벽하게 합리적으로 보이는 것 같습니다-이러한 종류의 모호함이 발생하는 것을 방지합니다.


1
좋은 지적. OP 또는 다른 사람들이 그것에 익숙하지 않은 경우 단락을 명시 적으로 언급 할 수 있습니다.
Chris Cooper

7
나는 그것을 생각하지 않았다. 제공된 예제에서 단락은 조건문 범위가 입력되는 것을 방지하지만,이 경우 처리되지 않는 변수를 선언하는 표현식 부분은 해당 범위가 조건 문의 범위로 제한되기 때문에 문제가되지 않습니다. 변수를 선언 한 표현식의 일부가 처리되지 않았을 때 조건문 범위가 입력 될 가능성이있는 경우에만 컴파일러가 오류를 발생 시키면 어떤 경우에 더 좋지 않을까요? 내가 준 예에서는 그렇지 않았습니다.
Neutrino 2011 년

@Neutrino 언뜻보기에 당신의 생각은 SAT 문제와 비슷하게 들리는데, 적어도 일반적인 경우에는 풀기가 쉽지 않습니다.
Christian Rau

5
if 조건에서 여러 변수 선언을 갖는 모든 문제에 대해 설명하고 제한된 방식으로 만 사용할 수 있다는 사실은 왜 이런 종류의 선언이 처음에 도입되었는지 궁금합니다. 나는 어떤 코드 예제에서 그것을보기 전에 그러한 구문이 필요하다고 느낀 적이 없었다 . 이 구문은 다소 어색하고 if 블록 앞에 변수를 선언하는 것이 훨씬 더 읽기 쉽다고 생각합니다. 해당 변수의 범위를 제한해야하는 경우 if 블록 주위에 추가 블록을 배치 할 수 있습니다. 이 구문을 사용한 적이 없습니다.
Giorgio

2
개인적으로 사용하는 변수의 범위를 추가 중첩 범위 지정 중괄호와 같은 추악한 측정 값에 의존하지 않고 사용해야하는 문 블록의 범위로 정확하게 제한 할 수 있다는 것이 다소 우아하다고 생각합니다.
Neutrino 2011 년

96

if또는 while문의 조건 은 expression 또는 단일 변수 선언 (초기화 포함) 일 수 있습니다.

두 번째와 세 번째 예제는 유효한 식도 아니고 유효한 선언도 아닙니다. 선언은 식의 일부를 구성 할 수 없기 때문입니다. 세 번째 예제와 같은 코드를 작성할 수 있으면 유용하지만 언어 구문을 크게 변경해야합니다.

나는 그것이 선언 주위에 괄호를 넣을 수 없다는 것에 대해 아무것도 말하지 않으며 조건 당 하나의 선언에 대해서만 말하지 않습니다.

6.4 / 1의 구문 사양은 조건에 대해 다음을 제공합니다.

condition:
    expression
    type-specifier-seq declarator = assignment-expression

괄호 나 기타 장식없이 단일 선언을 지정합니다.


3
이에 대한 이유나 배경이 있습니까?
Tomáš Zato-Monica 복원

23

더 좁은 범위로 변수를 묶으려면 항상 추가로 사용할 수 있습니다. { }

//just use { and }
{
    bool a = false, b = true;

    if(bool x = a || b)
    {
        //...
    }
}//a and b are out of scope

5
+1. 또한 x 선언을 주변 블록으로 옮길 것입니다. 왜 wrt a와 b라는 특별한 상태를 가져야합니까?
Giorgio

1
분명하지만 설득력이 없음 : 일반적인 루프 변수에 대해서도 마찬가지입니다. (허용됨, 제한된 변수 범위의 필요성은 루프에서 훨씬 더 일반적입니다.)
Peter-Monica 복원

18

마지막 섹션은 이미 작동하므로 약간 다르게 작성하면됩니다.

if (int a = Func1())
{
   if (int b = Func2())
   {
        // do stuff with a and b
   }
}

2

다음은 루프를 사용하는 추악한 해결 방법입니다 (두 변수가 모두 정수인 경우).

#include <iostream>

int func1()
{
    return 4;
}

int func2()
{
    return 23;
}

int main()
{
    for (int a = func1(), b = func2(), i = 0;
        i == 0 && a && b; i++)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }

    return 0;
}

그러나 이것은 다른 프로그래머를 혼란스럽게 할 것이고 오히려 나쁜 코드이므로 권장하지 않습니다.

간단한 둘러싸는 {}블록 (이미 권장 됨)이 훨씬 읽기 쉽습니다.

{
    int a = func1();
    int b = func2();

    if (a && b)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }
}

1

한 가지 주목할 점은 더 큰 if 블록 안의 표현식이

if (!((1 == 0) && (bool a = false)))

반드시 왼쪽에서 오른쪽으로 평가되는 것은 아닙니다. 그날 제가 겪었던 미묘한 버그 중 하나는 컴파일러가 실제로 왼쪽에서 오른쪽 대신 오른쪽에서 왼쪽으로 테스트한다는 사실과 관련이 있습니다.


5
그러나 오늘날 C99는 && 및 || 왼쪽에서 오른쪽으로 평가됩니다.
b0fh

2
단락 논리로 인해 인수에 대한 오른쪽에서 왼쪽으로의 평가가 불가능하다고 생각합니다. 그것은 항상 같은 단일 표현식에서 포인터와 pointee에 대한 테스트와 같은 것에 사용되었습니다 if(p && p->str && *p->str) .... 오른쪽에서 왼쪽은 치명적이었고 미묘하지 않았을 것입니다. 다른 운영자와 함께-심지어 할당! -그리고 함수 호출 인수는 맞지만 단락 연산자는 아닙니다.
피터 - 분석 재개 모니카

1

약간의 템플릿 마법으로 여러 변수를 선언 할 수없는 문제를 해결할 수 있습니다.

#include <stdio.h>

template <class LHS, class RHS>
struct And_t {
  LHS lhs;
  RHS rhs;

  operator bool () {
    bool b_lhs(lhs);
    bool b_rhs(rhs);
    return b_lhs && b_rhs;
  }
};
template <class LHS, class RHS> 
And_t<LHS, RHS> And(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }

template <class LHS, class RHS>
struct Or_t {
LHS lhs;
RHS rhs;

  operator bool () {
    bool b_lhs(lhs);
    bool b_rhs(rhs);
    return b_lhs || b_rhs;
  }
};
template <class LHS, class RHS> 
Or_t<LHS, RHS> Or(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }

int main() {
  if (auto i = And(1, Or(0, 3))) {
    printf("%d %d %d\n", i.lhs, i.rhs.lhs, i.rhs.rhs);
  }
  return 0;
}

(참고로 단락 평가가 느슨해집니다.)


나는 그것이 가정한다 손실 (또는 느슨하게?)
피터 - 분석 재개 모니카

5
나는 OP의 의도가 코드 간결성, 명확성 및 유지 보수성이라고 생각합니다. 당신이 제안한 "해결책"은 그 반대입니다.
Dženan 2017
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.