다음을 통해 공유


Visual Studio 2022의 C++ 규칙 향상, 동작 변경 및 버그 수정

Visual Studio의 Microsoft C/C++(MSVC)는 모든 릴리스에서 규칙을 개선하고 버그를 수정합니다. 이 문서에는 주 릴리스와 버전별 중요 개선 사항이 나와 있습니다. 특정 버전의 변경 내용으로 직접 이동하려면 이 문서의 맨 위에 있는 이 문서 내 링크를 사용합니다.

이 문서에는 Visual Studio 2022의 변경 내용이 나와 있습니다.

이전 버전의 Visual Studio 변경 내용:

버전 규칙 향상 링크
2019 Visual Studio 2019의 C++ 규칙 향상
2017 Visual Studio 2017의 C++ 규칙 향상
2003-2015 Visual C++ 2003~2015의 새로운 기능

Visual Studio 2022 버전 17.12의 규칙 향상

Visual Studio 2022 버전 17.12에는 Microsoft C/C++ 컴파일러에서 다음과 같은 규칙 향상, 버그 수정 및 동작 변경 내용이 포함되어 있습니다.

규칙 변경, 버그 수정 및 성능 향상을 포함하여 표준 템플릿 라이브러리의 변경 내용에 대한 자세한 요약은 STL Changelog VS 2022 17.12를 참조하세요.

_com_ptr_t::operator bool() 는 이제 명시적입니다.

원본/이진 호환성이 손상되는 변경입니다.

인스턴스에서 _com_ptr_t 암시적 변환 bool 은 놀랍거나 컴파일러 오류로 이어질 수 있습니다. 암시적 변환 함수는 C++ 핵심 지침(C.164)에 의해 권장되지 않으며 _com_ptr_t 두 가지 모두 bool Interface*에 대한 암시적 변환을 포함합니다. 이러한 두 암시적 변환은 모호성을 유발할 수 있습니다.

이 문제를 해결하기 위해 변환은 bool 이제 명시적입니다. 변환 Interface* 은 변경되지 않습니다.

이 새 동작을 옵트아웃하고 이전 암시적 변환을 복원하기 위한 매크로가 제공됩니다. /D_COM_DISABLE_EXPLICIT_OPERATOR_BOOL 이 변경 내용의 옵트아웃을 사용하여 컴파일합니다. 암시적 변환에 의존하지 않도록 코드를 수정하는 것이 좋습니다.

예시:

#include <comip.h>

template<class Iface>
using _com_ptr = _com_ptr_t<_com_IIID<Iface, &__uuidof(Iface)>>;

int main()
{
   _com_ptr<IUnknown> unk;
   if (unk) // Still valid
   { 
      // ...
   }
   bool b = unk; // Still valid.
   int v = unk; // Previously permitted, now emits C2240: cannot convert from '_com_ptr_t<_com_IIID<IUnknown,& _GUID_00000000_0000_0000_c000_000000000046>>' to 'int'
}

상수 식은 더 이상 항상 noexcept 허용 모드가 아닙니다.

원본/이진 호환성이 손상되는 변경입니다.

상수 식은 잠재적으로 throw되는 예외 사양으로 선언된 함수에 대한 함수 호출과 관련된 경우에도 항상 noexcept있었습니다. 이 단어는 C++17에서 제거되었지만 Microsoft Visual C++ 컴파일러는 여전히 모든 C++ 언어 버전에서 모드에서 /permissive 지원했습니다.

/permissive 모드 동작은 제거됩니다. 상수 식에는 더 이상 특별한 암시적 동작이 제공되지 않습니다.

noexcept 이제 함수의 constexpr 지정자가 모든 모드에서 적용됩니다. 이 변경은 표준 noexcept 동작을 사용하는 이후 핵심 문제 해결을 올바르게 구현하는 데 필요합니다.

예시:

constexpr int f(bool b) noexcept(false)
{ 
    if (b)
    {
        throw 1;
    }
    else
    {
        return 1;
    }
}

void g(bool b)
{
   noexcept(f(b)); // false. No change to behavior
   noexcept(f(true)); // false. No change to behavior
   noexcept(f(false)); // false. Was true in /permissive mode only in previous versions.
}

Visual Studio 2022 버전 17.11의 규칙 향상

Visual Studio 2022 버전 17.11에는 Microsoft C/C++ 컴파일러에서 다음과 같은 규칙 향상, 버그 수정 및 동작 변경이 포함됩니다.

규칙 변경, 버그 수정 및 성능 향상을 포함하여 표준 템플릿 라이브러리의 변경 내용에 대한 자세한 요약은 STL Changelog VS 2022 17.11을 참조하세요.

P3142R0 따라 이제 빈 줄을 쉽게 생성할 수 있습니다println. 이 기능은 .으로 /std:c++latest컴파일할 때 사용할 수 있습니다. 이 변경 전에, 당신은 썼다 : println(""); 지금 당신은 작성 : println();.

  • println();println(stdout);와 같습니다.
  • println(FILE* stream);println(stream, "\n");와 같습니다.

구현 range_formatter

이제 P2286R8range_formatter 단위로 구현됩니다. 이 기능은 .으로 /std:c++latest컴파일할 때 사용할 수 있습니다.

Visual Studio 2022 버전 17.10의 규칙 개선

Visual Studio 2022 버전 17.10에는 Microsoft C/C++ 컴파일러에서 다음과 같은 규칙 향상, 버그 수정 및 동작 변경 내용이 포함되어 있습니다.

규칙 변경, 버그 수정 및 성능 향상을 포함하여 표준 템플릿 라이브러리의 변경 내용에 대한 자세한 요약은 STL Changelog VS 2022 17.10을 참조하세요.

명시적으로 지정된 반환 형식을 사용한 변환 연산자 특수화

컴파일러가 변환 연산자를 잘못 특수화하여 반환 형식이 일치하지 않는 결과를 가져온 경우가 있었습니다. 이러한 잘못된 특수화는 더 이상 발생하지 않습니다. 이는 소스 코드의 호환성이 손상되는 변경입니다.

// Example 1
struct S
{
    template<typename T> operator const T*();
};

void test()
{
    S{}.operator int*(); // this is invalid now
    S{}.operator const int*(); // this is valid
}
// Example 2
// In some cases, the overload resolution result may change
struct S
{
    template <typename T> operator T*(); // overload 1
    template <typename T> operator const T*(); // overload 2
};

void test()
{
    S{}.operator int*(); // this used to call overload 2, now it calls overload 1
}

#elifdef#elifndef에 대한 지원이 추가되었습니다.

#elifdef#elifndef 전처리기 지시문을 도입한 WG21 P2334R1(C++23) 및 WG14 N2645(C++23)에 대한 지원이 추가되었습니다. /std:clatest 또는 /std:c++latest가 필요합니다.

이전:

#ifdef __cplusplus
  #include <atomic>
#elif !defined(__STDC_NO_ATOMICS__)
  #include <stdatomic.h>
#else
  #include <custom_atomics_library.h>
#endif

이후:

#ifdef __cplusplus
  #include <atomic>
#elifndef __STDC_NO_ATOMICS__
  #include <stdatomic.h>
#else
  #include <custom_atomics_library.h>
#endif

C의 구조적 형식에 _Alignas 적용

C 언어(C17 이상)에 적용됩니다. Microsoft Visual Studio 17.9에도 추가되었습니다.

Visual Studio 2022 버전 17.9 이전의 Visual C++ 버전에서 _Alignas 지정자가 선언의 구조적 형식 옆에 나타난 경우 ISO-C 표준에 따라 올바르게 적용되지 않았습니다.

// compile with /std:c17
#include <stddef.h>

struct Outer
{
    _Alignas(32) struct Inner { int i; } member1;
    struct Inner member2;
};
static_assert(offsetof(struct Outer, member2)==4, "incorrect alignment");

ISO-C 표준에 따르면 이 코드는 static_assert에서 진단을 내보내지 않고 컴파일되어야 합니다.

_Alignas 지시문은 멤버 변수 member1에만 적용됩니다. struct Inner 맞춤을 변경하지 않아야 합니다. 그러나 Visual Studio 17.9.1 이전에는 진단 "잘못된 맞춤"을 내보냈습니다. 컴파일러가 member2struct Outer 형식 내의 32바이트 오프셋에 맞췄습니다.

이진 호환성이 손상되는 변경이므로 이 변경 내용이 적용되면 경고가 내보내집니다. 경고 C5274는 이제 이전 예제인 warning C5274: behavior change: _Alignas no longer applies to the type 'Inner' (only applies to declared data objects)의 경고 수준 1에서 내보내집니다.

또한 이전 버전의 Visual Studio에서는 _Alignas 지정자가 익명 형식 선언 옆에 나타나면 무시되었습니다.

// compile with /std:c17
#include <stddef.h>
struct S
{
    _Alignas(32) struct { int anon_member; };
    int k;
};

static_assert(offsetof(struct S, k)==4, "incorrect offsetof");
static_assert(sizeof(struct S)==32, "incorrect size");

이전에는 이 코드를 컴파일할 때 두 static_assert 문이 모두 실패했습니다. 이제 코드가 컴파일되지만 다음 수준 1 경고를 내보냅니다.

warning C5274: behavior change: _Alignas no longer applies to the type '<unnamed-tag>' (only applies to declared data objects)
warning C5273: behavior change: _Alignas on anonymous type no longer ignored (promoted members will align)

이전 동작을 얻으려면 _Alignas(N)__declspec(align(N))으로 바꿉니다. _Alignas와 달리 declspec(align)이 해당 형식에 적용됩니다.

향상된 경고 C4706

이는 소스 코드의 호환성이 손상되는 변경입니다. 이전에는 할당이 의도된 경우 컴파일러가 조건식 내에서 할당에 대한 경고 C4706을 표시하지 않도록 할당을 괄호로 묶는 규칙을 검색하지 못했습니다. 이제 컴파일러는 괄호를 검색하고 경고를 표시하지 않습니다.

#pragma warning(error: 4706)

struct S
{
   auto mf()
   {
      if (value = 9)
         return value + 4;
      else
         return value;
   }

   int value = 9;
};

이제 컴파일러는 함수가 참조되지 않는 경우에도 경고를 내보냅니다. 이전에는 mf가 참조되지 않는 인라인 함수이므로 이 코드에 대해 경고 C4706을 내보내지 않았습니다. 이제 다음과 같은 경고를 내보냅니다.

error C4706: assignment used as a condition
note: if an assignment is intended you can enclose it in parentheses, '(e1 = e2)', to silence this warning

이 경고를 해결하려면 같음 연산자 value == 9를 사용합니다(의도된 경우). 또는 할당이 의도된 경우 할당을 괄호로 묶습니다. (value = 9). 그렇지 않으면 함수가 참조되지 않으므로 제거합니다.

Visual Studio 2022 버전 17.9의 규칙 개선

Visual Studio 2022 버전 17.9에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

표준 템플릿 라이브러리의 변경 내용에 대한 자세한 내용은 STL Changelog VS 2022 17.9를 참조하세요.

C의 구조적 형식에 _Alignas 적용

Visual Studio 2022 버전 17.9 이전의 Visual C++ 버전에서 _Alignas가 선언의 구조적 형식 옆에 나타난 경우 ISO-C 표준에 따라 올바르게 적용되지 않았습니다. 예시:

// compile with /std:c17
#include <stddef.h>
struct Outer
{
    _Alignas(32) struct Inner { int i; } member1;
    struct Inner member2;
};
static_assert(offsetof(struct Outer, member2)==4, "incorrect alignment");

ISO-C 표준에 따르면 이 코드는 static_assert에서 진단을 내보내지 않고 컴파일되어야 합니다. _Alignas 지시문은 멤버 변수 member1에만 적용됩니다. struct Inner 맞춤을 변경하지 않아야 합니다. 그러나 Visual Studio 17.9.1 릴리스 이전에는 진단 "잘못된 맞춤"을 내보냈습니다. 컴파일러는 member2struct Outer 내의 32 바이트 오프셋에 맞췄습니다.

이 문제를 해결하는 것은 이진 호환성이 손상되는 변경이므로 이 동작 변경이 적용되면 경고를 내보냅니다. 이전 코드의 경우 경고 C5274 "_Alignas는 더 이상 'Inner' 형식에 적용되지 않음(선언된 데이터 개체에만 적용됨)"이 경고 수준 1에서 내보내집니다.

이전 버전의 Visual Studio에서 _Alignas는 익명 형식 선언에 인접한 것으로 나타나면 무시되었습니다. 예시:

// compile with /std:c17
#include <stddef.h>
struct S {
    _Alignas(32) struct { int anon_member; };
    int k;
};
static_assert(offsetof(struct S, k)==4, "incorrect offsetof");
static_assert(sizeof(struct S)==32, "incorrect size");

이전에는 이 코드를 컴파일할 때 두 static_assert 문이 모두 실패했습니다. 이제 코드가 컴파일되지만 다음 수준 1 경고가 발생합니다.

warning C5274: behavior change: _Alignas no longer applies to the type '<unnamed-tag>' (only applies to declared data objects)
warning C5273: behavior change: _Alignas on anonymous type no longer ignored (promoted members will align)

이전 동작을 원하는 경우 _Alignas(N)__declspec(align(N))으로 바꿉니다. _Alignas와 달리 declspec(align)은 형식에 적용할 수 있습니다.

__VA_OPT__/Zc:preprocessor 아래에서 확장으로 사용하도록 설정됩니다.

__VA_OPT__가 C++20 및 C23에 추가되었습니다. 추가되기 전에는 variadic 매크로에서 쉼표를 유도하는 표준 방법이 없었습니다. 이전 버전과의 호환성을 높이기 위해 모든 언어 버전에서 토큰 기반 전처리기 /Zc:preprocessor에서 __VA_OPT__가 사용하도록 설정됩니다.

예를 들어 이제 오류 없이 컴파일됩니다.

#define LOG_WRAPPER(message, ...) WRITE_LOG(__LINE__, message __VA_OPT__(, __VA_ARGS__))

// Failed to build under /std:c11, now succeeds.
LOG_WRAPPER("Log message");
LOG_WRAPPER("Log message with %s", "argument")

C23 언어

C23의 경우 /std:clatest 컴파일러 스위치를 사용할 때 다음을 사용할 수 있습니다.

typeof
typeof_unqual

다음은 모든 C 언어 버전에서 사용할 수 있습니다.

__typeof__
__typeof_unqual__

C++ 표준 라이브러리

C++23 기능

  • P2286R8 서식 범위의 일부로 formattable, range_format, format_kindset_debug_format()
  • P0009R18에 따른 <mdspan> 및 C++23 표준에 적용된 및 후속 단어 변경 내용
  • P2510R3에 따른 format() 포인터

Visual Studio 2022 버전 17.8의 규칙 개선

Visual Studio 2022 버전 17.8에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

/FU에서 오류 발생

C 컴파일러는 한동안 관리형 컴파일을 지원하지 않더라도 /FU 옵션을 수락하는 데 사용되었습니다. 이제 오류가 발생합니다. 이 옵션을 전달하는 프로젝트는 C++/CLI 프로젝트로만 제한해야 합니다.

C++ 표준 라이브러리

이제 /std:c++20를 사용하여 컴파일할 때 C++23 명명된 모듈 stdstd.compat을 사용할 수 있습니다.

C++ 표준 라이브러리의 변경 내용에 대한 자세한 내용은 STL Changelog VS 2022 17.8을 참조하세요.

Visual Studio 2022 버전 17.7의 규칙 개선

Visual Studio 2022 버전 17.7에는 다음과 같은 주요 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

C 컴파일러에 /std:clatest가 추가됨

이 스위치는 C++ 컴파일러의 /std:c++latest 스위치와 같이 동작합니다. 이 스위치를 사용하면 현재 구현된 모든 컴파일러와 다음 C 표준 초안을 위해 제안된 표준 라이브러리 기능은 물론 일부 진행 중인 기능과 실험적인 기능을 사용할 수 있습니다.

C++ 표준 라이브러리

이제 <print> 라이브러리가 지원됩니다. P2093R14 서식 출력을 참조하세요.

views::cartesian_product가 구현되었습니다.

표준 템플릿 라이브러리의 변경 내용에 대한 자세한 내용은 STL Changelog VS 2022 17.7을 참조하세요.

using 규칙

이전에는 using 지시문으로 인해 사용된 네임스페이스의 이름이 표시되지 않아야 하는데 계속 표시될 수 있었습니다. 이로 인해 using 지시문이 활성화되지 않은 경우에도 정규화되지 않은 이름 조회가 네임스페이스에서 이름을 찾을 수 있습니다.

다음은 새 동작과 이전 동작의 몇 가지 예입니다.
"(1)"에 대한 다음 주석의 참조는 네임스페이스 Af<K>(t) 호출을 의미합니다.

namespace A
{ 
    template<typename K, typename T> 
    auto f2(T t)
    { 
        return f<K>(t); // (1) Unqualified lookup should not find anything
    } 
} 

namespace B
{ 
    template<typename K, typename T> 
    auto f(T t) noexcept
    { // Previous behavior: This function was erroneously found during unqualified lookup at (1)
        return A::f2<K>(t); 
    } 
} 

namespace C
{ 
    template<typename T> 
    struct S {}; 

    template<typename, typename U> 
    U&& f(U&&) noexcept; // New behavior: ADL at (1) correctly finds this function 
} 

namespace D
{ 
    using namespace B; 

    void h()
    { 
        D::f<void>(C::S<int>()); 
    } 
} 

동일한 기본 문제로 인해 이전에 컴파일된 코드가 이제 거부될 수 있습니다.

#include <memory>
namespace Addin {}
namespace Gui
{
    using namespace Addin;
}

namespace Addin
{
    using namespace std;
}

// This previously compiled, but now emits error C2065 for undeclared name 'allocator'.
// This should be declared as 'std::allocator<T*>' because the using directive nominating
// 'std' is not active at this point.
template <class T, class U = allocator<T*>>
class resource_list
{
};

namespace Gui
{
    typedef resource_list<int> intlist;
}

Visual Studio 2022 버전 17.6의 규칙 개선

Visual Studio 2022 버전 17.6에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

복합 volatile 할당은 더 이상 사용되지 않습니다.

C++20에서는 volatile로 한정된 형식에 더 이상 특정 연산자를 적용하지 않습니다. 예를 들어 다음 코드는 cl /std:c++20 /Wall test.cpp로 컴파일됩니다.

void f(volatile int& expr)
{
   ++expr;
}

컴파일러는 test.cpp(3): warning C5214: applying '++' to an operand with a volatile qualified type is deprecated in C++20을 생성합니다.

C++20에서는 복합 할당 연산자(@=형식의 연산자)가 더 이상 사용되지 않습니다. C++23에서는 C++20에서 제외된 복합 연산자가 더 이상 사용되지 않습니다. 예를 들어 C++23에서 다음 코드는 경고를 생성하지 않지만 C++20에서는 경고를 생성합니다.

void f(volatile int& e1, int e2)
{
   e1 += e2;
}

이 변경에 대한 자세한 내용은 CWG:2654를 참조하세요.

식에서 같음을 다시 써도 호환성이 손상되는 변경(P2468R2)은 적게 발생합니다.

C++20에서 P2468R2는 다음과 같은 코드를 수락하도록 컴파일러를 변경했습니다.

struct S
{
    bool operator==(const S&);
    bool operator!=(const S&);
};
bool b = S{} != S{};

컴파일러는 이 코드를 허용합니다. 즉, 컴파일러가 다음과 같은 코드를 사용하면 더 엄격해집니다.

struct S
{
  operator bool() const;
  bool operator==(const S&);
};

bool b = S{} == S{};

컴파일러 버전 17.5는 이 프로그램을 허용합니다. 컴파일러 버전 17.6은 이 프로그램을 거부합니다. 이 문제를 해결하려면 constoperator==에 추가하여 모호성을 제거합니다. 또는 다음 예제와 같이 해당 operator!=를 정의에 추가합니다.

struct S
{
  operator bool() const;
  bool operator==(const S&);
  bool operator!=(const S&);
};

bool b = S{} == S{};

Microsoft C/C++ 컴파일러 버전 17.5 및 17.6은 이전 프로그램을 수락하고 두 버전 모두에서 S::operator==를 호출합니다.

P2468R2 설명된 일반적인 프로그래밍 모델은 형식에 해당하는 operator!=가 있는 경우 일반적으로 다시 쓰기 동작을 표시하지 않는다는 것입니다. 해당 operator!=를 추가하는 것은 이전에 C++17에서 컴파일된 코드에 대한 권장 수정 사항입니다. 자세한 내용은 프로그래밍 모델을 참조하세요.

Visual Studio 2022 버전 17.4의 규칙 개선

Visual Studio 2022 버전 17.4에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

고정 형식이 없는 범위가 지정되지 않은 enum의 기본 형식

Visual Studio 2022 버전 17.4 이전 버전의 Visual Studio에서 C++ 컴파일러는 고정된 기본 형식이 없는 범위가 지정되지 않은 열거형의 기본 형식을 정확하게 결정하지 못했습니다. /Zc:enumTypes에서 이제 표준 동작을 올바르게 구현합니다.

C++ 표준에서는 enum의 기본 형식이 해당 enum의 모든 열거자를 저장할 수 있을 만큼 충분히 커야 합니다. 충분히 큰 열거자는 enum의 기본 형식을 unsigned int, long long 또는 unsigned long long로 설정할 수 있습니다. 이전에는 이러한 enum 형식이 항상 열거자 값에 관계없이 Microsoft 컴파일러의 기본 형식 int를 가졌습니다.

사용하도록 설정하면 /Zc:enumTypes 옵션은 잠재적인 원본 및 이진 호환성이 손상되는 변경입니다. 수정 사항이 이진 호환성에 영향을 줄 수 있으므로 기본값으로 꺼져 있고 /permissive-에서 사용하도록 설정되지 않습니다. 일부 열거형 형식은 규칙 수정을 사용하도록 설정하면 크기가 변경됩니다. 특정 Windows SDK 헤더에는 이러한 열거형 정의가 포함됩니다.

예제

enum Unsigned
{
    A = 0xFFFFFFFF // Value 'A' does not fit in 'int'.
};

// Previously, failed this static_assert. Now passes with /Zc:enumTypes.
static_assert(std::is_same_v<std::underlying_type_t<Unsigned>, unsigned int>);

template <typename T>
void f(T x)
{
}

int main()
{
    // Previously called f<int>, now calls f<unsigned int>.
    f(+A);
}

// Previously this enum would have an underlying type of `int`, but Standard C++ requires this to have
// a 64-bit underlying type. Using /Zc:enumTypes changes the size of this enum from 4 to 8, which could
// impact binary compatibility with code compiled with an earlier compiler version or without the switch.
enum Changed
{
    X = -1,
    Y = 0xFFFFFFFF
};

고정된 기본 형식이 없는 enum 정의 내의 열거자 형식

Visual Studio 2022 버전 17.4 이전 버전의 Visual Studio에서는 C++ 컴파일러가 열거자 유형을 올바르게 모델링하지 않았습니다. 열거형의 닫는 중괄호 앞에 고정된 기본 형식이 없는 열거형에서 잘못된 형식을 가정할 수 있습니다. /Zc:enumTypes에서 컴파일러는 이제 표준 동작을 올바르게 구현합니다.

C++ 표준은 고정된 기본 형식이 없는 열거형 정의 내에서 이니셜라이저가 열거자 형식을 결정하도록 지정합니다. 또는 이니셜라이저가 없는 열거자의 경우 이전 열거자의 형식으로 지정합니다(오버플로를 고려). 이전에는 이러한 열거자에 항상 기본 형식(일반적으로 int)에 대한 자리 표시자와 함께 추론된 열거형 형식이 주어졌습니다.

사용하도록 설정하면 /Zc:enumTypes 옵션은 잠재적인 원본 및 이진 호환성이 손상되는 변경입니다. 수정 사항이 이진 호환성에 영향을 줄 수 있으므로 기본값으로 꺼져 있고 /permissive-에서 사용하도록 설정되지 않습니다. 일부 열거형 형식은 규칙 수정을 사용하도록 설정하면 크기가 변경됩니다. 특정 Windows SDK 헤더에는 이러한 열거형 정의가 포함됩니다.

예시

enum Enum {
    A = 'A',
    B = sizeof(A)
};

static_assert(B == 1); // previously failed, now succeeds under /Zc:enumTypes

이 예제에서 열거자 A는 열거형의 닫는 중괄호 앞에 형식 char가 있어야 하므로 Bsizeof(char)를 사용하여 초기화해야 합니다. /Zc:enumTypes 수정 전에는 A가 추론된 기본 형식 int인 열거형 형식 Enum을 사용했으며, Bsizeof(Enum) 또는 4를 사용하여 초기화되었습니다.

Visual Studio 2022 버전 17.3의 규칙 개선

Microsoft Visual StudioVisual Studio 2022 버전 17.3에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

C: 포인터 간의 한정자 호환성 검사 개선

C 컴파일러가 포인터 간 한정자 특히 void*를 제대로 비교하지 못했습니다. 이 결함으로 인해 const int**void* 간의 비호환성 및 int* volatile*void* 간의 호환성이 잘못 진단될 수 있습니다.

예제

void fn(void* pv) { (pv); }

int main()
{
    int t = 42;
    int* pt = &t;
    int* volatile * i = &pt;
    fn(i);    // Now raises C4090
    const int** j = &pt;
    fn(j);    // No longer raises C4090
}

Visual Studio 2022 버전 17.2의 규칙 개선

Visual Studio 2022 버전 17.2에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

종결되지 않은 양방향 문자 경고

Visual Studio 2022 버전 17.2에는 주석 및 문자열에서 종결되지 않은 유니코드 양방향 문자에 대해 수준 3 경고 C5255가 추가됩니다. 이 경고는 Nicholas Boucher 및 Ross Anderson의 Trojan Source: Invisible Vulnerabilities에 설명된 보안 문제를 해결합니다. 유니코드 양방향 문자에 대한 자세한 내용은 Unicode® Standard Annex #9: UNICODE BIDIRECTIONAL ALGORITHM을 참조하세요.

경고 C5255는 변환 후 유니코드 양방향 문자를 포함하는 파일만 해결합니다. 이 경고는 UTF-8, UTF-16 및 UTF-32 파일에 적용되므로 적절한 소스 인코딩을 제공해야 합니다. 이는 원본 호환성이 손상되는 변경입니다.

예제(이전/이후)

Visual Studio 2022 버전 17.2 이전의 Visual Studio 버전에서는 종결되지 않은 양방향 문자가 경고를 생성하지 않았습니다. Visual Studio 2022 버전 17.2는 C5255 경고를 생성합니다.

// bidi.cpp
int main() {
    const char *access_level = "user";
    // The following source line contains bidirectional Unicode characters equivalent to:
    //    if ( strcmp(access_level, "user\u202e \u2066// Check if admin \u2069 \u2066") ) {
    // In most editors, it's rendered as:
    //    if ( strcmp(access_level, "user") ) { // Check if admin
    if ( strcmp(access_level, "user‮ ⁦// Check if admin ⁩ ⁦") ) {
        printf("You are an admin.\n");
    }
    return 0;
}

/* build output
bidi.cpp(8): warning C5255: unterminated bidirectional character encountered: 'U+202e'
bidi.cpp(8): warning C5255: unterminated bidirectional character encountered: 'U+2066'
*/

from_chars() float 타이브레이커

Visual Studio 2022 버전 17.2에서 <charconv> from_chars() float 타이브레이커 규칙에서 잘못된 결과를 생성한 버그가 수정되었습니다. 이 버그는 좁은 범위 내에서 연속한 float 값의 정확히 중간점에 있던 10진수 문자열에 영향을 주었습니다. (영향을 받는 값이 가장 작고 가장 큰 값은 각각 및 131071.98828125가장 큰 값입니다32768.009765625.) 타이 브레이커 규칙은 "짝수"로 반올림하고 싶었고 "짝수"는 "다운"되었지만 구현이 잘못 반올림되었습니다 (double영향을받지 않았습니다.) 자세한 내용 및 구현 세부 정보는 microsoft/STL#2366을 참조하세요.

이 변경은 지정된 사례 범위의 런타임 동작에 영향을 줍니다.

예시

// from_chars_float.cpp
#include <cassert>
#include <charconv>
#include <cstdio>
#include <string_view>
#include <system_error>
using namespace std;
int main() {
    const double dbl  = 32768.009765625;
    const auto sv     = "32768.009765625"sv;
    float flt         = 0.0f;
    const auto result = from_chars(sv.data(), sv.data() + sv.size(), flt);
    assert(result.ec == errc{});
    printf("from_chars() returned: %.1000g\n", flt);
    printf("This rounded %s.\n", flt < dbl ? "DOWN" : "UP");
}

Visual Studio 2022 버전 17.2 이전:

C:\Temp>cl /EHsc /nologo /W4 /std:c++17 from_chars_float.cpp && from_chars_float
from_chars_float.cpp
from_chars() returned: 32768.01171875
This rounded UP.

Visual Studio 2022 버전 17.2 이상:

C:\Temp>cl /EHsc /nologo /W4 /std:c++17 from_chars_float.cpp && from_chars_float
from_chars_float.cpp
from_chars() returned: 32768.0078125
This rounded DOWN.

/Zc:__STDC__는 C에 __STDC__를 제공합니다.

C 표준에서는 준수 C 구현이 __STDC__1로 정의해야 합니다. __STDC__1일 때 POSIX 함수를 노출하지 않는 UCRT의 동작으로 인해, 안정적인 언어 버전에 호환성이 손상되는 변경을 도입하지 않고는 기본적으로 C에 대해 이 매크로를 정의할 수 없습니다. Visual Studio 2022 버전 17.2 이상에서는 이 매크로를 정의하는 규칙 옵션 /Zc:__STDC__가 추가되었습니다. 음수 버전의 옵션은 없습니다. 현재 C의 이후 버전에 기본적으로 이 옵션을 사용할 계획을 세우고 있습니다.

이는 원본 호환성이 손상되는 변경입니다. C11 또는 C17 모드(/std:c11 또는 /std:c17)를 사용하도록 설정하고 /Zc:__STDC__이(가) 지정된 경우에 적용됩니다.

예시

// test__STDC__.c
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
#if __STDC__
    int f = _open("file.txt", _O_RDONLY);
    _close(f);
#else
    int f = open("file.txt", O_RDONLY);
    close(f);
#endif
}

/* Command line behavior

C:\Temp>cl /EHsc /W4 /Zc:__STDC__ test__STDC__.c && test__STDC__

*/

누락된 중괄호에 대한 경고

C5246 경고는 하위 개체의 집계 초기화 중에 누락된 중괄호를 보고합니다. Visual Studio 2022 버전 17.2 이전에서는 경고가 익명 struct 또는 union의 사례를 처리하지 못했습니다.

이는 원본 호환성이 손상되는 변경입니다. 기본적으로 해제된 C5246 경고를 사용하도록 설정하면 적용됩니다.

예제

Visual Studio 2022 버전 17.2 이상에서 이 코드는 이제 오류를 발생시킵니다.

struct S {
   union {
      float f[4];
      double d[2];
   };
};

void f()
{
   S s = { 1.0f, 2.0f, 3.14f, 4.0f };
}

/* Command line behavior
cl /Wall /c t.cpp

t.cpp(10): warning C5246: 'anonymous struct or union': the initialization of a subobject should be wrapped in braces
*/

이 문제를 해결하려면 이니셜라이저에 중괄호를 추가하세요.

void f()
{
   S s = { { 1.0f, 2.0f, 3.14f, 4.0f } };
}

Visual Studio 2022 버전 17.1의 규칙 개선

Visual Studio 2022 버전 17.1에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

로컬이 아닌 람다 식에서 잘못된 형식의 캡처 기본값 감지

C++ 표준은 블록 범위의 람다 식에 캡처 기본값만 허용합니다. Visual Studio 2022 버전 17.1 이상에서, 컴파일러는 로컬이 아닌 람다 식에서 캡처 기본값이 허용되지 않는 경우를 감지합니다. 새 수준 4 경고인 C5253을 생성합니다.

이는 원본 호환성이 손상되는 변경입니다. 해당 변경 사항은 새로운 람다 프로세서를 사용하는 모든 모드, 즉 /Zc:lambda, /std:c++20 또는 /std:c++latest에 적용됩니다.

예제

Visual Studio 2022 버전 17.1에서 이제 이 코드는 오류를 내보냅니다.

#pragma warning(error:5253)

auto incr = [=](int value) { return value + 1; };

// capture_default.cpp(3,14): error C5253: a nonlocal lambda cannot have a capture default
// auto incr = [=](int value) { return value + 1; };
//              ^

이 문제를 해결하려면 다음 캡처 기본값을 제거합니다.

#pragma warning(error:5253)

auto incr = [](int value) { return value + 1; };

C4028은 이제 함수 포인터 작업에 대한 C4133임

Visual Studio 2022 버전 17.1 이전에는 컴파일러가 C 코드의 특정 포인터 함수 비교에서 잘못된 오류 메시지를 보고했습니다. 이 잘못된 메시지는 인수 개수가 같지만 유형이 호환되지 않는 두 함수 포인터를 비교했을 때 보고되었습니다. 이제 함수 매개 변수 불일치가 아닌, 포인터 함수 비호환성이 있음을 알리는 다른 경고를 표시합니다.

이는 원본 호환성이 손상되는 변경입니다. 해당 변경 사항은 코드가 C로 컴파일될 때 적용됩니다.

예시

int f1(int); 
int f2(char*); 
int main(void) 
{ 
    return (f1 == f2); 
}
// Old warning:
// C4028: formal parameter 1 different from declaration
// New warning:
// C4113: 'int (__cdecl *)(char *)' differs in parameter lists from 'int (__cdecl *)(int)'

종속적이지 않은 static_assert에서의 오류

Visual Studio 2022 버전 17.1 이상에서는 static_assert와 연결된 식이 종속 식이 아닌 경우, 컴파일러에서는 구문 분석될 때 평가합니다 식이 false로 계산되는 경우 컴파일러에서 오류를 내보냅니다. 이전에는 static_assert가 함수 템플릿 본문 또는 클래스 템플릿 본문 내에 있을 경우 컴파일러에서 이러한 분석을 실시하지 않았습니다.

이는 원본 호환성이 손상되는 변경입니다. 해당 변경 사항은 /permissive- 또는 /Zc:static_assert를 의미하는 모든 모드에 적용됩니다. 이 동작 변경 사항은 /Zc:static_assert- 컴파일러 옵션을 통해 사용하지 않도록 설정할 수 있습니다.

예제

Visual Studio 2022 버전 17.1 이상에서 이 코드는 이제 오류를 발생시킵니다.

template<typename T>
void f()
{
   static_assert(false, "BOOM!");
}

이 문제를 해결하려면 식을 종속으로 만듭니다. 예를 들면 다음과 같습니다.

template<typename>
constexpr bool dependent_false = false;

template<typename T>
void f()
{
   static_assert(dependent_false<T>, "BOOM!");
}

이 변경 사항으로 컴파일러는 함수 템플릿 f가 인스턴스화되었을 경우에만 오류를 내보냅니다.

Visual Studio 2022 버전 17.0의 규칙 향상

Visual Studio 2022 버전 17.0에는 다음과 같은 Microsoft C/C++ 컴파일러의 규칙 향상, 버그 수정 및 동작 변경이 포함되어 있습니다.

열거형 형식의 비트 필드 너비에 대한 경고

열거형 형식의 인스턴스를 비트 필드로 선언하는 경우 비트 필드의 너비는 열거형의 가능한 모든 값을 수용해야 합니다. 그렇지 않으면 컴파일러가 진단 메시지를 표시합니다. 다음 예제를 고려해보세요.

enum class E : unsigned { Zero, One, Two };

struct S {
  E e : 1;
};

한 프로그래머가 클래스 멤버 S::e에 명시적으로 명명된 enum 값이 있다고 예상할 수 있습니다. 열거형 요소의 수를 고려할 때 이는 불가능합니다. 비트 필드는 E(개념적으로 E의 ‘도메인’)의 명시적으로 제공된 값 범위를 포함할 수 없습니다. 비트 필드 너비가 열거형의 도메인에 대해 충분히 크지 않다는 문제를 해결하기 위해 새 경고(기본적으로 해제)가 MSVC에 추가됩니다.

t.cpp(4,5): warning C5249: 'S::e' of type 'E' has named enumerators with values that cannot be represented in the given bit field width of '1'.
  E e : 1;
    ^
t.cpp(1,38): note: see enumerator 'E::Two' with value '2'
enum class E : unsigned { Zero, One, Two };
                                     ^

이 컴파일러 동작은 모든 /std/permissive 모드에 영향을 미치는 소스 및 호환성이 손상되는 이진 변경입니다.

nullptr 또는 0에 대해 정렬된 포인터 비교 시 오류 발생

C++ Standard에서는 nullptr 또는 0에 대해 정렬된 포인터 비교를 실수로 허용했습니다. 예를 들면 다음과 같습니다.

bool f(int *p)
{
   return p >= 0;
}

WG21 문서 N3478에서는 이러한 실수가 제거되었습니다. 이 변경 내용은 MSVC에서 구현됩니다. 예제를 /permissive- (및 /diagnostics:caret )를 사용하여 컴파일하면 다음 오류가 발생합니다.

t.cpp(3,14): error C7664: '>=': ordered comparison of pointer and integer zero ('int *' and 'int')
    return p >= 0;
             ^

이 컴파일러 동작은 모든 /std 모드에서 /permissive- 를 사용하여 컴파일된 코드에 영향을 미치는 소스 및 호환성이 손상되는 이진 변경입니다.

참조

Microsoft C/C++ 언어 규칙