"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.