/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++20 版的選項會隱含設定 /std:c++latest 選項。 /permissive- C++20 模組支援需要。 您的程式碼可能不需要模組支援,但需要在 或 /std:c++latest 底下 /std:c++20 啟用其他功能。 您可以使用 選項來明確啟用 Microsoft 延伸模組支援 /permissive ,而不需要尾端虛線。 選項 /permissive 必須位在隱含設定 /permissive- 的任何選項之後。

根據預設,此選項 /permissive- 會在 Visual Studio 2017 15.5 版和更新版本所建立的新專案中設定。 它預設不會在舊版中設定。 設定選項時,編譯器會在程式碼中偵測到非標準語言建構時產生診斷錯誤或警告。 這些建構包含 C++11 程式碼中的一些常見 Bug。

從 Windows Fall Creators SDK (10.0.16299.0) 開始,此選項 /permissive- 與最新 Windows 套件中幾乎所有標頭檔相容,例如軟體發展工具組 (SDK) 或 Windows 驅動程式套件 (WDK)。 舊版 SDK 可能因為各種原始程式碼一致性原因而無法編譯 /permissive- 。 編譯器和 SDK 會以不同的發行時間軸寄送,因此仍有一些剩餘的問題。 如需特定標頭檔問題,請參閱 下方的 Windows 標頭問題

選項會將 /permissive-/Zc:referenceBinding/Zc:strictStrings/Zc:rvalueCast 選項設定為符合行為。 這些選項預設為不符合規範的行為。 您可以在命令列上 /permissive- 傳遞特定的 /Zc 選項,以覆寫此行為。

在 Visual Studio 2017 15.3 版開始的編譯器版本中, /permissive- 選項會設定 /Zc:ternary 選項。 編譯器也會實作兩階段名稱查閱的更多需求。 /permissive-設定選項時,編譯器會剖析函式和類別範本定義,並識別範本中使用的相依和非相依名稱。 在此版本中,只會執行名稱相依性分析。

自 Visual Studio 2022 Update 17.6 起, /permissive- 選項會設定 /Zc:lambda/Zc:externConstexpr 選項。 在舊版中, /permissive- 未設定其中一個。

標準保留至實作的環境特定延伸模組和語言區域不會受到 /permissive- 影響。 例如,Microsoft 特定的 __declspec 、呼叫慣例和結構化例外狀況處理關鍵字,以及編譯器特定的 pragma 指示詞或屬性不會在模式中 /permissive- 由編譯器加上旗標。

舊版 Visual Studio 2017 中的 MSVC 編譯器不支援所有 C++11、C++14 或 C++17 符合標準的程式碼。 根據 Visual Studio 的版本, /permissive- 此選項可能不會在兩階段名稱查閱的某些方面偵測到問題、系結暫存的非常數參考、將 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.
}

您可以使用 ,獨立 /permissive/Zc:hiddenFriend 啟用隱藏的朋友名稱查閱規則。 如果您想要隱藏的朋友名稱查閱的舊版行為,但否則想要 /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 版之前的編譯器版本中,編譯器接受標準視為模棱兩可的條件運算子(或三元運算子) ?: 引數。 在 /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 中的程式碼會在 使用 或 /std:c++14/Zc:ternary/permissive- 接受,並在 或 更新版本下 /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__))

您也可以在範本中繼程式編譯中看到錯誤,其中條件運算子結果類型可能會在 和 /permissive-/Zc:ternary 變更。 解決此問題的其中一個方法是在 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

兩階段名稱查閱

/permissive-設定選項時,編譯器會剖析函式和類別範本定義,並識別範本中使用的相依和非相依名稱,以作為兩階段名稱查閱的必要專案。 在 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();
}

如果您想要進行兩階段查閱的舊版行為,但否則想要 /permissive- 行為,請新增 /Zc:twoPhase- 選項。

Windows 標頭問題

在 Windows Fall Creators Update SDK(10.0.16299.0) 或 Windows 驅動程式套件 1709 版之前,此選項 /permissive- 過於嚴格。 建議您更新為最新版本的 Windows 套件,以在 /permissive- Windows 或裝置驅動程式程式碼中使用。

Windows 2018 年 4 月更新 SDK(10.0.17134.0)、Windows Fall Creators Update SDK (10.0.16299.0) 或 Windows 驅動程式套件 (WDK) 1709 中的某些標頭檔,仍然有與 使用 /permissive- 不相容的問題。 若要解決這些問題,建議您限制使用這些標頭只使用這些標頭的原始程式碼檔案,並在編譯這些特定原始程式碼檔案時移除 /permissive- 選項。

這些在 Windows 2018 年 4 月更新 SDK 中發行的 WinRT WRL 標頭(10.0.17134.0) 不會使用 /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- 編譯器參數時, tagRESTRICTION 結構不會編譯,因為 case(RTOr) 成員 or

    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;
    };
    

    若要解決此問題,請編譯不含 選項的 /permissive- 檔案 Query.h

  • 中的問題 um/cellularapi_oem.h

    當您使用 /permissive- 編譯器參數時,的正向宣告 enum UICCDATASTOREACCESSMODE 會造成警告:

    typedef enum UICCDATASTOREACCESSMODE UICCDATASTOREACCESSMODE; // C4471
    

    未限定範圍的 enum 正向宣告是 Microsoft 延伸模組。 若要解決此問題,請編譯不含 選項的 /permissive- 檔案 cellularapi_oem.h ,或使用 /wd 選項將警告 C4471 靜音。

  • 中的問題 um/omscript.h

    在 C++03 中,從字串常值轉換成 BSTR (這是 typedef 到 wchar_t * ) 的轉換已被取代,但允許。 在 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
    

    若要解決此問題,請編譯不含 選項的 omscript.h /permissive- 檔案,或改用 /Zc:strictStrings-

在 Visual Studio 開發環境中設定這個編譯器選項

在 Visual Studio 2017 15.5 版和更新版本中,使用此程式:

  1. 開啟專案的 [屬性頁] 對話方塊。

  2. 選取 [ 組態屬性 > C/C++ > 語言] 屬性頁。

  3. [一致性模式 ] 屬性值變更為 [是] (/permissive-)。 選擇 [確定 ] 或 [ 套用 ] 以儲存您的變更。

在 Visual Studio 2017 15.5 版之前的版本中,請使用下列程式:

  1. 開啟專案的 [屬性頁] 對話方塊。

  2. 選取 [組態屬性]>[C/C++]>[命令列] 屬性頁。

  3. 在 [ 其他選項 ] 方塊中輸入 /permissive- 編譯器選項。 選擇 [確定 ] 或 [ 套用 ] 以儲存您的變更。

若要以程式方式設定這個編譯器選項

另請參閱

MSVC 編譯器選項
MSVC 編譯器命令列語法