"if"문에서 변수 초기화


80

나는 C ++ 17에서 if다음과 같은 문 에서 변수를 초기화 할 수 있다는 것을 읽었습니다.

if (int length = 2; length == 2)
    //execute something

대신에

int length = 2;
if (length == 2)
    //do something

더 짧지 만 코드 가독성에 영향을 미치며 (특히이 새로운 기능을 모르는 사람들에게) 대규모 소프트웨어 개발에 나쁜 코딩 관행이라고 생각합니다.

코드를 짧게 만드는 것 외에이 기능을 사용하면 어떤 이점이 있습니까?


38
스코프 외에?
— DeiDei

10
누군가 몇 년 전 "C ++ 11에서는 이렇게 람다 문을 만들 수 있다는 것을 읽었습니다. (...) 더 짧지 만 코드 가독성에 영향을 미칩니다 (특히 모르는 사람들에게 이 새로운 기능)은 대규모 소프트웨어 개발에 나쁜 코딩 관행이라고 생각합니다. "
— R2RT

9
나는 그것이 짧지 않고 정확히 같은 길이라고 말하고 싶습니다.
— user7860670

5
순수한 의견이므로 답이 아닙니다 if (int length = 2; length == 2). 처음 보는 것은 놀라운 일이 아닐 수 있지만 이해할 수없는 복잡한 것은 아니므로 이미 두 번째는 더 이상 큰 놀라움이 아니며 그것이 속한 범위에서 물건을 선언합니다. 가독성에 기여하는 주요 요인 중 하나입니다. 이럴 당신의 전제는 잘못된 것입니다)
— largest_prime_is_463035818

14
코드가 작성된 언어 ( "이 새로운 기능을 모른다"가 의미하는 것)를 모르는 사람들이 코드 가독성에 대해 걱정하는 것은 바닥을 향한 경쟁입니다.

답변:


97

그것은의 범위를 제한 length받는 사람 if혼자. 그래서 우리가 글을 쓸 수 있었을 때 우리가 원래 얻었던 것과 같은 혜택을 얻습니다

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

변수 누출 대신

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

몇 가지 이유로 수명이 짧은 변수가 더 좋습니다. 하지만 몇 가지를 말하면 :

  1. 수명이 짧을수록 관련없는 코드 줄을 읽을 때 염두에 두어야 할 사항이 줄어 듭니다. 경우 i루프 또는 외부에 존재하지 않는 if문, 우리는 그들의 가치의 외부 마음을 할 필요가 없습니다. 또한 그 값이 의도 된 범위를 벗어난 프로그램의 다른 부분과 상호 작용할 것이라고 걱정할 필요가 없습니다 ( i위가 다른 루프에서 재사용되는 경우 발생할 수 있음 ). 코드를 따르고 추론하기가 더 쉽습니다.

  2. 변수에 자원이 있으면 해당 자원은 이제 가능한 가장 짧은 기간 동안 보유됩니다. 그리고 이것은 불필요한 중괄호가 없습니다. 또한 자원이 if혼자 와 관련이 있음이 분명합니다 . 이것을 동기를 부여하는 예로 고려하십시오

    if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
    }
    

동료가 기능을 인식하지 못하는 경우 가르치십시오! 배우고 싶지 않은 프로그래머를 달래는 것은 기능을 피하는 핑계가되지 않습니다.


12
이 마지막 문장을 2 미터 높이의 포스터에칩니다.
— Quentin

3
또한 수명이 짧은 (엄격한 범위) 변수는 목적이 제공되면 나중에 코드에서 실수로 변수를 재사용 할 수 없기 때문에 버그를 줄여야합니다.
— Galik

1
또는 약한 포인터로 :if (auto p = ptr.lock(); p && p->foo()) bar(*p);
— Deduplicator 19

1
3. 컴파일러는 더 많은 경우에 스택 공간을 재사용 할 수 있습니다. (예를 들어 참조 또는 외부 함수에 대한 포인터로 i를 전달한 경우)
— TLW

더 나은 질문은 "이것의 장점이 무엇인지"일 수 있습니다 {int i = 2; if (i == 2) {...}}(추가 범위에주의하십시오.)
— TLW

24

코드를 짧게 만드는 것 외에이 기능을 사용하면 어떤 이점이 있습니까?

변수 범위를 줄입니다. 이것은 당신이 추론해야 할 식별자의 지역성을 강화하기 때문에 의미가 있고 가독성을 증가시킵니다. 명령문 내부의 긴 init 문 if은 피해야 한다는 데 동의 하지만 짧은 내용은 괜찮습니다.

C ++ 17 이전의 결과에 대해 이미 초기화 및 분기를 수행 할 수 있습니다.

int *get(); // returns nullptr under some condition

if (int *ptr = get())
    doStuff();

이것은 개인의 의견에 따라 다르지만 명시적인 조건을 더 읽기 쉽게 고려할 수 있습니다.

if (int *ptr = get(); ptr != nullptr)
    doStuff();

게다가, 사람들이 익숙하지 않다는 사실을 언급하여 기능의 가독성에 반대하는 것은 위험합니다. 사람들은 어느 시점에서 스마트 포인터에 익숙하지 않았지만 오늘날에도 우리 모두는 그들이 거기에있는 것이 좋은 것이라고 동의합니다.


4
당신이 사용할 수있는 if (auto p =get ())연산자 부울이 정의되어 있기 때문에
— sudo는 RF RM은 슬래시

19

새로운 형태의 if 문은 많은 용도로 사용됩니다.

현재 이니셜 라이저는 문 앞에 선언되어 주변 범위로 유출되거나 명시 적 범위가 사용됩니다. 새로운 형식을 사용하면 이러한 코드를 더 간결하게 작성할 수 있으며 개선 된 범위 제어로 인해 오류가 발생하기 쉬운 일부 구성이 좀 더 강력 해집니다.

이니셜 라이저가있는 If 문에 대한 개방형 표준 제안

여기에 이미지 설명 입력

따라서 요약하면이 문은 일반적인 코드 패턴을 단순화하고 사용자가 범위를 좁게 유지하는 데 도움이됩니다.

도움이 되었기를 바랍니다.


제안서에서 인용하고 있음을 분명히 해주시겠습니까? 특히 두 번째 단락. 나는 인용구를 제안한다.
— 이야기꾼 - Unslander 모니카

감사합니다 @StoryTeller, 예, C ++ 17에 통합 된 open-std 제안의 두 번째 단락을 인용했습니다.
— 아비 쉑 신하

10

변수의 범위를 최소화하기 위해 생성시 유효한 경우에만 리소스를 정의하는 관용구가 있습니다 (예 : 파일 스트림 객체 ) :

if(auto file = std::ifstream("filename"))
{
    // use file here
}
else
{
    // complain about errors here
}

// The identifier `file` does not pollute the wider scope

때로는 실패를 기본 절로 만들고 유효한 리소스를 절로 만들기 위해 해당 테스트의 논리를 역전시킬 수 있기를 원합니다 else. 이전에는 불가능했습니다. 하지만 이제 우리는 할 수 있습니다 :

if(auto file = std::ifstream("filename"); !file)
{
    // complain about errors here
}
else
{
    // use file here
}

예를 들어 예외가 발생할 수 있습니다.

if(auto file = std::ifstream(filename); !file)
    throw std::runtime_error(std::strerror(errno));
else
{
    // use file here
}

어떤 사람들 은 오류가 발생 하면 함수가 조기 에 중단 되고 그렇지 않으면 진행 되도록 코딩하는 것을 좋아합니다 . 이 관용구는 어떤 사람들이 더 자연스럽게 생각할 수있는 연속 논리 위에 물리적으로 중단 논리를 배치합니다.


8

논리적 이벤트에 특히 유용합니다. 이 예를 고려하십시오.

char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
    std::cerr << "bad stuff\n";
}

약간 거칠어 보입니다. OR, AND부정에 대해 잘 알고 있지 않다면 이 논리에 대해 잠시 멈추고 생각해야 할 수도 있습니다. 일반적으로 잘못된 설계입니다. 으로 if-initialization당신은 표현력을 추가 할 수 있습니다.

char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
    std::cerr << "bad stuff\n";
} 

명명 된 변수는 내부에서도 재사용 할 수 있습니다 if. 예 :

if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
    std::cerr << distance << " is too small\n";
}

이것은 특히 변수의 범위가 지정되어 나중에 공간을 오염시키지 않는다는 점을 감안할 때 훌륭합니다.


2
주관적이라는 것을 알고 있지만 if-initializer가있는 버전보다 "거친"버전을 강력하게 선호합니다. 읽고 이해하기가 더 쉽습니다.
— 파비오는 분석 재개 모니카 말한다

@FabioTurati 나는 당신이 그것에 매우 익숙하고 다른 버전이 새롭기 때문이라고 생각합니다. 그러나 시간이 지남에 따라 if-initializer가 비슷한 것을 능가 할 것으로 기대합니다.
— Stack Danny

7

이것은 내 경험의 가독성을 돕는 기존 기능의 확장입니다.

if (auto* ptr = get_something()) {
}

여기에서 우리는 둘 다 변수를 만들고 ptrnull이 아닌지 테스트합니다. 의 범위 ptr는 유효한 위치로 제한됩니다. 의 모든 사용 ptr이 타당 하다는 것을 스스로 확신하는 것이 훨씬 쉽습니다 .

그러나 우리가 bool그런 식으로 전환되지 않는 것에 대해 이야기하고 있다면 어떨까요?

if (auto itr = find(bob)) {
}

작동하지 않습니다. 그러나이 새로운 기능을 통해 다음을 수행 할 수 있습니다.

if (auto itr = find(bob); itr != end()) {
}

"이 초기화가 유효 할 때"라는 절을 추가하십시오.

본질적으로 이것은 "일부 표현식을 초기화하고 유효하면 코드를 수행하십시오. 유효하지 않으면 폐기하십시오"라는 의미의 토큰 세트를 제공합니다.

C ++ 98 이후로 포인터 테스트 트릭을 수행하는 것은 관용적이었습니다. 일단 그것을 받아 들인다면,이 확장은 당연합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.