[Warning] C26819

연두비두밥·2024년 4월 24일
post-thumbnail

  • 코딩을 하다가 C26819 경고가 발생했다.
    찾아보니깐, VS 2019 16.7 버전에 실수 방지를 위한 코딩 규칙이 추가되었다고 한다.(현재 vs 2022버전 사용중)
    내가 발생한 경고가 그중 한가지이다.

1. Switch 문에 default 라벨이 없는 경우
2. switch 문에 의도를 알기 어려운 fallthrough가 존재할 경우
3. range-based for문에서의 높은 비용의 복사 시
4. auto 키워드와 함께 높은 비용의 복사 시

위와 같은 4가지의 경우에 경고를 발생한다.

1. Switch 문에 default 라벨이 없는 경우

switch (val)
{
	case 1:
    	std::cout<<"1";
       	break;
    case 2:
    	std::cout<<"2";
        break;
}
  • 위와 같이 마지막에 default를 사용하지 않으면 버그를 야기하기 쉽다.
    그렇기 때문에 default가 없다는 사항을 경고로 알려준다.

2. Switch 문에 의도를 알기 어려운 fallthrough가 존재할 경우

  • 이 경우가 내가 발생한 경우이다. 물론 내 코드에서는 해당 내용을 의도하고 작성하였다.
switch (val)
{
	case 1:
    	std::cout<<"1";
       	break;
    case 2:
    case 3: // warning
    	std::cout<<"this is";
        // [[fallthrough]]
    case 4:
    	std::cout<<"2";
        break;
}
  • fallthrough란 switch 문에서 임의의 case에 break를 사용하지 않는 것을 의미한다. 즉, 위의 코드에서 case 3: 일 경우 "this is" 를 출력하고 쭉 떨어져서 case 4:를 실행해 "2"를 출력하는 것을 의미한다.
    물론 나는 의도하고 코드를 작성했으나, 만약 의도가 아니라 break를 작성하는 것을 잊어버린 실수라면 예의치 않은 문제를 발생시킨다. 그렇기 때문에 경고를 발생시킨다.
    의도를 명확히 하기 위해 [[fallthrough]] 키워드를 추가해준다.
    즉, 나는 의도를 가지고 고의로 아래의 함수까지 실행하는 것이다. 라는 것을 명시해준다.

3. range-based for문에서의 높은 비용의 복사 (warning C26817)

  • 범위 기반 for문은 매우 편리하게 특정 컨테이너의 요소를 사용할 수 있다.
void emailEvertoneInCompany(const std::vector<Person>& employees)
{
	Email email;
   	for(Person p : employees)
    {
   // Warning C26817: Potentially expensive copy of variable 'p' in range-for loop.
    	email.addRecipient(p.email_address);
    }
  • 현재의 코드에서 for문은 employess의 각 요소를 p에 저장하게 되는데, 이때 p는 복사된 값을 사용한다. 즉, 반복될때마다 복사된다. 복사본 비용이 많이 드는 경우 비효율성의 원인이 될 수 있다. 불필요한 복사본을 해결하기 위해 새로운 C++ Core Check 규칙을 추가하여 복사본을 제거하는 방법을 제시했고 한다.
void emailEvertoneInCompany(const std::vector<Person>& employees)
{
	Email email;
   	for(const Person& p : employees)
    {
    	email.addRecipient(p.email_address);
    }
  • 이처럼 사용하면 복사를 하지 않는다.
    근데 뭔가 형태가 너무 익숙하다 코딩하다보면 사실 범위 기반 for문을 돌게 되면 const auto& 가 기본으로 작성했는데, 복사때문인건 알고 있었지만 복사를 하게 되면 경고를 주는 것은 새롭게 알게 된 것 같다.

  • 여기서 의문인것은 "높은 비용"이다. "높은 비용"의 기준이 무엇일까?
    -> 크기가 플랫폼 종속 포인터 크기의 두배보다 크고 스마트 포인터나 gsl::span,gsl::string_spanstd::string_viewPerson이 아닌 경우 복사본의 비용이 많이 드는 것으로 간주한다.

즉, 숫자, 스칼라 등과 같은 작은 데이터 유형의 경우에는 경고가 발생하지 않는다.
추가적으로 루프 안에서 변수의 변경이 일어나면 검사가 실시되지 않는다. 유의해야한다. 복사가 일어나므로 변수의 변경을 실시하는 경우는 복사된 값의 변경이라 이런 방식으로 사용하는 일은 거의 없다고 생각한다. 결론은 꼭 참조를 붙이자~

4. auto 키워드와 함께 높은 비용의 복사 (warning C26820)

  • auto 키워드는 코딩을 하게 되면, 정말 많이 사용하게 되는데 이는 컴파일러에게 형식을 추론하게 맡기는 것이다.
    이런 경우는 예상치 못한 실수가 나올수도 있다.
    아래의 예시를 살펴보자
class PasswordManager
{
	std::string password;
public:
	const std::string& getPassword() const 
    { return password;}
};
void stealPassword(const PasswordManager& pm)
{
 // Warning C26820: Assigning by value when a const-reference would suffice,
    // use const auto& instead.
	auto password = pm.getPassword();
}

위 클래스는 getPassword는 const 참조 타입을 반환한다. stealPassword 함수에서 auto 키워드로 pm의 객체를 받는다. 컴파일러는 password가 참조로 받을지 값으로 받을지 판단하지 못한다. 하지만 이는 수정하지도 않고 그냥 받아오는 경우는 복사가 일어나고 위에 언급했던 "높은 비용" 이 발생하면 경고를 발생한다.
이러한 경고는 const 참조가 효율적이라 판단해 const auto&로 받도록 하는 것이다.

마무리

  • 평소에 자주 사용하는 문법들이 꽤 많이 보이는데, 다시한번 경고들과 같이 보니깐 정리가 되는 것들이 꽤 보인다.
  • 이펙티브 C++에도 나오지만 틈만나면 const를 들이대봐야한다. (특히, const auto&의 경우는 너무 자주쓴다.)

참고사이트_Microsoft

profile
꾸준하고 싶은 사람

0개의 댓글