/permissive- (표준 규칙)

컴파일러에 대한 표준 규칙 모드를 지정합니다. 코드에서 규칙 문제를 식별하고 수정하여 보다 정확하고 이식성이 향상되도록 하려면 이 옵션을 사용합니다.

구문

/permissive-
/permissive

설명

/permissive- 옵션은 Visual Studio 2017 이상에서 지원됩니다. /permissive 는 Visual Studio 2019 버전 16.8 이상에서 지원됩니다.

컴파일러 옵션을 사용하여 /permissive- 표준 준수 컴파일러 동작을 지정할 수 있습니다. 이 옵션은 허용 동작을 사용하지 않도록 설정하고 엄격한 준수를 /Zc 위해 컴파일러 옵션을 설정합니다. IDE에서 이 옵션은 IntelliSense 엔진이 비준수 코드에 밑줄을 표시합니다.

/permissive- 옵션은 현재 컴파일러 버전의 규칙 지원을 사용하여 비준수 언어 구문을 결정합니다. 이 옵션은 코드가 특정 버전의 C++ 표준을 준수하는지 여부를 결정하지 않습니다. 최신 초안 표준에 대해 구현된 모든 컴파일러 지원을 사용하도록 설정하려면 이 /std:c++latest 옵션을 사용합니다. 컴파일러 지원을 현재 구현된 C++20 표준으로 제한하려면 이 /std:c++20 옵션을 사용합니다. 컴파일러 지원을 현재 구현된 C++17 표준으로 제한하려면 이 /std:c++17 옵션을 사용합니다. 컴파일러 지원이 C++14 표준과 더 밀접하게 일치하도록 제한하려면 기본값인 옵션을 사용합니다 /std:c++14 .

/permissive- 옵션은 Visual Studio 2019 버전 16.8부터, 버전 16.11에서 옵션으로 암시적으로 설정 /std:c++latest 됩니다 /std:c++20 . /permissive- 는 C++20 모듈 지원에 필요합니다. 코드에 모듈 지원이 필요하지 않지만 아래 또는 /std:c++latest아래에서 사용하도록 설정된 /std:c++20 다른 기능이 필요할 수 있습니다. 후행 대시 없이 옵션을 사용하여 /permissive Microsoft 확장 지원을 명시적으로 사용하도록 설정할 수 있습니다. 옵션은 /permissive 암시적으로 설정하는 /permissive- 옵션 후에 와야 합니다.

기본적으로 이 /permissive- 옵션은 Visual Studio 2017 버전 15.5 이상 버전에서 만든 새 프로젝트에서 설정됩니다. 이전 버전에서는 기본적으로 설정되지 않습니다. 옵션을 설정하면 코드에서 비표준 언어 구문이 검색되면 컴파일러가 진단 오류 또는 경고를 생성합니다. 이러한 구문에는 C++11 이전 코드의 몇 가지 일반적인 버그가 포함됩니다.

/permissive- 옵션은 Windows Fall Creators SDK(10.0.16299.0)부터 SDK(소프트웨어 개발 키트) 또는 WDK(Windows 드라이버 키트)와 같은 최신 Windows 키트의 거의 모든 헤더 파일과 호환됩니다. 이전 버전의 SDK는 다양한 소스 코드 규칙 이유로 인해 컴파일 /permissive- 되지 않을 수 있습니다. 컴파일러와 SDK는 다른 릴리스 타임라인 제공되므로 몇 가지 다시 기본 문제가 있습니다. 특정 헤더 파일 문제는 아래의 Windows 헤더 문제를 참조하세요.

/permissive- 옵션은 , /Zc:strictStrings/Zc:rvalueCast 옵션을 준수 동작으로 설정합니다/Zc:referenceBinding. 이러한 옵션은 기본적으로 비준수 동작으로 설정됩니다. 명령줄에서 특정 /Zc 옵션을 /permissive- 전달하여 이 동작을 재정의할 수 있습니다.

Visual Studio 2017 버전 15.3 /permissive- 부터 시작하는 컴파일러 버전에서 이 옵션은 옵션을 설정합니다 /Zc:ternary . 또한 컴파일러는 2단계 이름 조회에 대한 더 많은 요구 사항을 구현합니다. /permissive- 옵션을 설정하면 컴파일러는 함수 및 클래스 템플릿 정의를 구문 분석하고 템플릿에 사용되는 종속 및 비 종속 이름을 식별합니다. 이 릴리스에서는 이름 종속성 분석만 수행됩니다.

Visual Studio 2022 업데이트 17.6을 /permissive- 기준으로 이 옵션은 옵션 및 /Zc:externConstexpr 옵션을 설정합니다/Zc:lambda. 이전 버전 /permissive- 에서는 둘 중 하나를 설정하지 않았습니다.

표준에서 구현까지 남겨 둔 환경별 확장 및 언어 영역은 영향을 /permissive-받지 않습니다. 예를 들어 Microsoft 관련 __declspec호출 규칙 및 구조적 예외 처리 키워드(keyword) 및 컴파일러별 pragma 지시문 또는 특성은 컴파일러에 의해 모드로 /permissive- 플래그가 지정되지 않습니다.

이전 버전의 Visual Studio 2017의 MSVC 컴파일러는 모든 C++11, C++14 또는 C++17 표준 준수 코드를 지원하지 않습니다. Visual Studio /permissive- 버전에 따라 이 옵션은 2단계 이름 조회의 일부 측면에서 문제를 감지하지 못할 수도 있고, 비컨스트 참조를 임시로 바인딩하거나, copy init를 직접 init로 처리하거나, 초기화에서 여러 사용자 정의 변환을 허용하거나, 논리 연산자 및 기타 지원되지 않는 규칙 영역에 대한 대체 토큰을 허용하지 않을 수 있습니다. Visual C++의 규칙과 관련된 문제에 대한 자세한 내용은 Nonstandard Behavior을 참조하세요. 최대한 활용 /permissive-하려면 Visual Studio를 최신 버전으로 업데이트합니다.

코드를 수정하는 방법

다음은 문제를 해결하는 제안된 방법과 함께 사용할 /permissive-때 비준수로 검색되는 코드의 몇 가지 예입니다.

네이티브 코드에서 식별자로 사용 default

void func(int default); // Error C2321: 'default' is a keyword, and
                        // cannot be used in this context

종속 기반에서 멤버 조회

template <typename T>
struct B
{
    void f() {}
    template <typename U>
    struct S { void operator()(){ return; } };
};

template <typename T>
struct D : public B<T> // B is a dependent base because its type
                       // depends on the type of T.
{
    // One possible fix for non-template members and function
    // template members is a using statement:
    // using B<T>::f;
    // If it's a type, don't forget the 'typename' keyword.

    void g()
    {
        f(); // error C3861: 'f': identifier not found
        // Another fix is to change the call to 'this->f();'
    }

    void h()
    {
        S<int> s; // C2065 or C3878
        // Since template S is dependent, the type must be qualified
        // with the `typename` keyword.
        // To fix, replace the declaration of s with:
        // typename B<T>::template S<int> s;
        // Or, use this:
        // typename D::template S<int> s;
        s();
    }
};

void h() {
    D<int> d;
    d.g();
    d.h();
}

멤버 선언에서 정규화된 이름 사용

struct A {
    void A::f() { } // error C4596: illegal qualified name in member
                    // declaration.
                    // Remove redundant 'A::' to fix.
};

멤버 이니셜라이저에서 여러 공용 구조체 멤버 초기화

union U
{
    U()
        : i(1), j(1) // error C3442: Initializing multiple members of
                     // union: 'U::i' and 'U::j'.
                     // Remove all but one of the initializations to fix.
    {}
    int i;
    int j;
};

숨겨진 친구 이름 조회 규칙

클래스 외부의 선언은 숨겨진 친구를 표시할 수 있습니다.

// Example 1
struct S {
    friend void f(S *);
};
// Uncomment this declaration to make the hidden friend visible:
// void f(S *); // This declaration makes the hidden friend visible

using type = void (*)(S *);
type p = &f; // error C2065: 'f': undeclared identifier.

리터럴 nullptr 을 사용하면 인수 종속 조회를 방지할 수 있습니다.

// Example 2
struct S {
    friend void f(S *);
};
void g() {
    // Using nullptr instead of S prevents argument dependent lookup in S
    f(nullptr); // error C3861: 'f': identifier not found

    S *p = nullptr;
    f(p); // Hidden friend now found via argument-dependent lookup.
}

를 사용하여 /Zc:hiddenFriend숨겨진 친구 이름 조회 규칙을 독립적으로 /permissive 사용하도록 설정할 수 있습니다. 숨겨진 친구 이름 조회에 대한 레거시 동작을 원하지만 그렇지 않으면 동작을 원하는 /permissive- 경우 이 /Zc:hiddenFriend- 옵션을 사용합니다.

배열 범위에서 범위가 지정된 열거형 사용

enum class Color {
    Red, Green, Blue
};

int data[Color::Blue]; // error C3411: 'Color' is not valid as the size
                       // of an array as it is not an integer type.
                       // Cast to type size_t or int to fix.

네이티브 코드의 각 코드에 사용

void func() {
    int array[] = {1, 2, 30, 40};
    for each (int i in array) // error C4496: nonstandard extension
                              // 'for each' used: replace with
                              // ranged-for statement:
                              // for (int i: array)
    {
        // ...
    }
}

ATL 특성 사용

Microsoft 관련 ATL 특성은 다음에서 /permissive-문제를 일으킬 수 있습니다.

// Example 1
[uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")]
class A {};

대신 양식을 사용하여 __declspec 문제를 해결할 수 있습니다.

// Fix for example 1
class __declspec(uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")) B {};

더 복잡한 예는 다음과 같습니다.

// Example 2
[emitidl];
[module(name="Foo")];

[object, local, uuid("9e66a290-4365-11d2-a997-00c04fa37ddb")]
__interface ICustom {
    HRESULT Custom([in] longl, [out, retval] long*pLong);
    [local] HRESULT CustomLocal([in] longl, [out, retval] long*pLong);
};

[coclass, appobject, uuid("9e66a294-4365-11d2-a997-00c04fa37ddb")]
class CFoo : public ICustom
{};

해결하려면 추가 빌드 단계가 필요합니다. 이 경우 IDL 파일을 만듭니다.

// Fix for example 2
// First, create the *.idl file. The vc140.idl generated file can be
// used to automatically obtain a *.idl file for the interfaces with
// annotation. Second, add a midl step to your build system to make
// sure that the C++ interface definitions are outputted.
// Last, adjust your existing code to use ATL directly as shown in
// the atl implementation section.

-- IDL  FILE--
import "docobj.idl";

[object, local, uuid(9e66a290-4365-11d2-a997-00c04fa37ddb)]
interface ICustom : IUnknown {
    HRESULT Custom([in] longl, [out,retval] long*pLong);
    [local] HRESULT CustomLocal([in] longl, [out,retval] long*pLong);
};

[ version(1.0), uuid(29079a2c-5f3f-3325-99a1-3ec9c40988bb) ]
library Foo {
    importlib("stdole2.tlb");
    importlib("olepro32.dll");

    [version(1.0), appobject, uuid(9e66a294-4365-11d2-a997-00c04fa37ddb)]
    coclass CFoo { interface ICustom; };
}

-- ATL IMPLEMENTATION--
#include <idl.header.h>
#include <atlbase.h>

class ATL_NO_VTABLE CFooImpl : public ICustom,
    public ATL::CComObjectRootEx<CComMultiThreadModel>
{
    public:BEGIN_COM_MAP(CFooImpl)
    COM_INTERFACE_ENTRY(ICustom)
    END_COM_MAP()
};

모호한 조건부 연산자 인수

Visual Studio 2017 버전 15.3 이전의 컴파일러 버전에서 컴파일러는 표준에서 모호한 것으로 간주되는 조건부 연산자(또는 3차 연산자) ?: 에 대한 인수를 수락했습니다. 모드에서 /permissive- 컴파일러는 이전 버전에서 진단 없이 컴파일된 경우 하나 이상의 진단 발급합니다.

이 변경으로 인해 발생할 수 있는 일반적인 오류는 다음과 같습니다.

  • error C2593: 'operator ?' is ambiguous

  • error C2679: binary '?': no operator found which takes a right-hand operand of type 'B' (or there is no acceptable conversion)

  • error C2678: binary '?': no operator found which takes a left-hand operand of type 'A' (or there is no acceptable conversion)

  • error C2446: ':': no conversion from 'B' to 'A'

이 문제를 일으킬 수 있는 일반적인 코드 패턴은 일부 클래스 C 가 다른 형식 T 의 명시적이지 않은 생성자와 형식으로의 비 명시적 변환 연산자를 모두 제공하는 경우 T입니다. 두 번째 인수를 세 번째 인수의 형식으로 변환하는 것은 유효한 변환입니다. 세 번째 인수를 두 번째 인수의 형식으로 변환하는 작업도 마찬가지입니다. 둘 다 유효하므로 표준에 따라 모호합니다.

// Example 1: class that provides conversion to and initialization from some type T
struct A
{
    A(int);
    operator int() const;
};

extern bool cond;

A a(42);
// Accepted when /Zc:ternary or /permissive- is not used:
auto x = cond ? 7 : a; // A: permissive behavior prefers A(7) over (int)a
// Accepted always:
auto y = cond ? 7 : int(a);
auto z = cond ? A(7) : a;

T가 null로 끝나는 문자열 형식(예const char *const char16_t *: 등) 중 하나를 나타내고 실제 인수 ?: 가 해당 형식의 문자열 리터럴인 경우 이 일반적인 패턴에는 중요한 예외가 있습니다. C++17이 C++14에서 의미 체계를 변경했습니다. 결과적으로 예제 2의 코드는 사용되거나 사용될 때 /Zc:ternary/permissive- 아래 또는 그 이상에서 /std:c++14/std:c++17 허용되고 거부됩니다.

// Example 2: exception from the above
struct MyString
{
    MyString(const char* s = "") noexcept;  // from char*
    operator const char* () const noexcept; //   to char*
};

extern bool cond;

MyString s;
// Using /std:c++14, /permissive- or /Zc:ternary behavior
// is to prefer MyString("A") over (const char*)s
// but under /std:c++17 this line causes error C2445:
auto x = cond ? "A" : s;
// You can use a static_cast to resolve the ambiguity:
auto y = cond ? "A" : static_cast<const char*>(s);

하나의 형식 void인수가 있는 조건부 연산자에서도 오류가 표시 될 수 있습니다. 이 경우 ASSERT와 유사한 매크로에서 일반적일 수 있습니다.

// Example 3: void arguments
void myassert(const char* text, const char* file, int line);
// Accepted when /Zc:ternary or /permissive- is not used:
#define ASSERT_A(ex) (void)((ex) ? 1 : myassert(#ex, __FILE__, __LINE__))
// Accepted always:
#define ASSERT_B(ex) (void)((ex) ? void() : myassert(#ex, __FILE__, __LINE__))

조건부 연산자 결과 형식이 다음과 /Zc:ternary/permissive-같이 변경될 수 있는 템플릿 메타프로그래밍에서 오류가 표시될 수도 있습니다. 이 문제를 해결하는 한 가지 방법은 결과 형식에서 사용하는 std::remove_reference 것입니다.

// Example 4: different result types
extern bool cond;
extern int count;
char  a = 'A';
const char  b = 'B';
decltype(auto) x = cond ? a : b; // char without, const char& with /Zc:ternary
const char (&z)[2] = count > 3 ? "A" : "B"; // const char* without /Zc:ternary

2단계 이름 조회

/permissive- 옵션을 설정하면 컴파일러는 함수 및 클래스 템플릿 정의를 구문 분석하여 2단계 이름 조회에 필요한 대로 템플릿에 사용되는 종속 및 비 종속 이름을 식별합니다. Visual Studio 2017 버전 15.3에서는 이름 종속성 분석이 수행됩니다. 특히 템플릿 정의의 컨텍스트에서 선언되지 않은 비 종속 이름은 ISO C++ 표준에 필요한 진단 메시지를 발생합니다. Visual Studio 2017 버전 15.7에서는 정의 컨텍스트에서 인수 종속 조회가 필요한 비 종속 이름의 바인딩도 수행됩니다.

// dependent base
struct B {
    void g() {}
};

template<typename T>
struct D : T {
    void f() {
        // The call to g was incorrectly allowed in VS2017:
        g();  // Now under /permissive-: C3861
        // Possible fixes:
        // this->g();
        // T::g();
    }
};

int main()
{
    D<B> d;
    d.f();
}

2단계 조회에 대한 레거시 동작을 원하지만 그렇지 않은 경우 동작을 원하는 /permissive- 경우 옵션을 추가합니다 /Zc:twoPhase- .

Windows 헤더 문제

Windows /permissive- Fall Creators Update SDK(10.0.16299.0) 또는 WDK(Windows 드라이버 키트) 버전 1709 이전의 Windows 키트 버전에는 이 옵션이 너무 엄격합니다. Windows 또는 디바이스 드라이버 코드에서 사용할 /permissive- 최신 버전의 Windows 키트로 업데이트하는 것이 좋습니다.

Windows 2018년 4월 업데이트 SDK(10.0.17134.0), Windows Fall Creators Update SDK(10.0.16299.0) 또는 WDK(Windows 드라이버 키트) 1709의 /permissive-특정 헤더 파일에는 여전히 사용과 호환되지 않는 문제가 있습니다. 이러한 문제를 해결하려면 이러한 헤더의 사용을 필요한 소스 코드 파일로만 제한하고 특정 소스 코드 파일을 컴파일할 때 옵션을 제거하는 /permissive- 것이 좋습니다.

Windows 2018년 4월 업데이트 SDK(10.0.17134.0)에서 릴리스된 이러한 WinRT WRL 헤더는 클린 /permissive-없습니다. 이러한 문제를 해결하려면 다음 헤더를 사용하지 /permissive-않거나 이러한 헤더로 /Zc:twoPhase- 작업할 때 사용합니다/permissive-.

  • 의 문제 winrt/wrl/async.h

    C:\Program Files (x86)\Windows Kits\10\Include\10.0.17134.0\winrt\wrl\async.h(483): error C3861: 'TraceDelegateAssigned': identifier not found
    C:\Program Files (x86)\Windows Kits\10\Include\10.0.17134.0\winrt\wrl\async.h(491): error C3861: 'CheckValidStateForDelegateCall': identifier not found
    C:\Program Files (x86)\Windows Kits\10\Include\10.0.17134.0\winrt\wrl\async.h(509): error C3861: 'TraceProgressNotificationStart': identifier not found
    C:\Program Files (x86)\Windows Kits\10\Include\10.0.17134.0\winrt\wrl\async.h(513): error C3861: 'TraceProgressNotificationComplete': identifier not found
    
  • 의 문제 winrt/wrl/implements.h

    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\winrt\wrl\implements.h(2086): error C2039: 'SetStrongReference': is not a member of 'Microsoft::WRL::Details::WeakReferenceImpl'
    

Windows 2018년 4월 업데이트 SDK(10.0.17134.0)에서 릴리스된 이러한 사용자 모드 헤더는 클린 /permissive-없습니다. 이러한 문제를 해결하려면 다음 헤더를 사용할 때 사용하지 /permissive- 마세요.

  • 의 문제 um/Tune.h

    C:\ProgramFiles(x86)\Windows Kits\10\include\10.0.17134.0\um\tune.h(139): error C3861: 'Release': identifier not found
    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um\tune.h(559): error C3861: 'Release': identifier not found
    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um\tune.h(1240): error C3861: 'Release': identifier not found
    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um\tune.h(1240): note: 'Release': function declaration must be available as none of the arguments depend on a template parameter
    
  • 의 문제 um/spddkhlp.h

    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um\spddkhlp.h(759): error C3861: 'pNode': identifier not found
    
  • 의 문제 um/refptrco.h

    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um\refptrco.h(179): error C2760: syntax error: unexpected token 'identifier', expected 'type specifier'
    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um\refptrco.h(342): error C2760: syntax error: unexpected token 'identifier', expected 'type specifier'
    C:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um\refptrco.h(395): error C2760: syntax error: unexpected token 'identifier', expected 'type specifier'
    

이러한 문제는 Windows Fall Creators Update SDK(10.0.16299.0)의 사용자 모드 헤더와 관련이 있습니다.

  • 의 문제 um/Query.h

    컴파일러 스위치를 /permissive- 사용하는 경우 멤버ortagRESTRICTION 인해 구조가 case(RTOr) 컴파일되지 않습니다.

    struct tagRESTRICTION
    {
         ULONG rt;
         ULONG weight;
         /* [switch_is][switch_type] */ union _URes
         {
             /* [case()] */ NODERESTRICTION ar;
             /* [case()] */ NODERESTRICTION or;  // error C2059: syntax error: '||'
             /* [case()] */ NODERESTRICTION pxr;
             /* [case()] */ VECTORRESTRICTION vr;
             /* [case()] */ NOTRESTRICTION nr;
             /* [case()] */ CONTENTRESTRICTION cr;
             /* [case()] */ NATLANGUAGERESTRICTION nlr;
             /* [case()] */ PROPERTYRESTRICTION pr;
             /* [default] */  /* Empty union arm */
         } res;
    };
    

    이 문제를 해결하려면 옵션 없이 포함된 Query.h 파일을 컴파일합니다 /permissive- .

  • 의 문제 um/cellularapi_oem.h

    컴파일러 스위치를 /permissive- 사용하면 전달 선언 enum UICCDATASTOREACCESSMODE 으로 인해 경고가 발생합니다.

    typedef enum UICCDATASTOREACCESSMODE UICCDATASTOREACCESSMODE; // C4471
    

    범위 enum 가 지정되지 않은 전방 선언은 Microsoft 확장입니다. 이 문제를 해결하려면 옵션 없이 /permissive- 포함된 cellularapi_oem.h 파일을 컴파일하거나 경고 C4471을 침묵하는 옵션을 사용합니다/wd.

  • 의 문제 um/omscript.h

    C++03에서는 문자열 리터럴에서 typedefwchar_t *BSTR 의 변환은 사용되지 않지만 허용됩니다. C++11에서는 변환이 더 이상 허용되지 않습니다.

    virtual /* [id] */ HRESULT STDMETHODCALLTYPE setExpression(
         /* [in] */ __RPC__in BSTR propname,
         /* [in] */ __RPC__in BSTR expression,
         /* [in][defaultvalue] */ __RPC__in BSTR language = L"") = 0; // C2440
    

    이 문제를 해결하려면 옵션 없이 /permissive- omscript.h를 포함하는 파일을 컴파일하거나 대신 사용합니다 /Zc:strictStrings- .

Visual Studio 개발 환경에서 이 컴파일러 옵션을 설정하려면

Visual Studio 2017 버전 15.5 이상 버전에서는 다음 절차를 사용합니다.

  1. 프로젝트의 속성 페이지 대화 상자를 엽니다.

  2. 구성 속성>C/C++>Language 속성 페이지를 선택합니다.

  3. 규칙 모드 속성 값을 Yes(/permissive-)로 변경합니다. 확인을 선택하거나 적용하여 변경 내용을 저장합니다.

Visual Studio 2017 버전 15.5 이전 버전에서는 다음 절차를 사용합니다.

  1. 프로젝트의 속성 페이지 대화 상자를 엽니다.

  2. 구성 속성>C/C++>명령줄 속성 페이지를 선택합니다.

  3. 추가 옵션 상자에 /permissive- 컴파일러 옵션을 입력합니다. 확인을 선택하거나 적용하여 변경 내용을 저장합니다.

프로그래밍 방식으로 이 컴파일러 옵션을 설정하려면

참고 항목

MSVC 컴파일러 옵션
MSVC 컴파일러 명령줄 구문