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,하지만 조건 while 은 if.
— 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.