Bagikan melalui


/permissive- (Kesuaian standar)

Tentukan mode kesuaian standar ke pengkompilasi. Gunakan opsi ini untuk membantu Anda mengidentifikasi dan memperbaiki masalah kesamaan dalam kode Anda, untuk membuatnya lebih benar dan lebih portabel.

Sintaks

/permissive-
/permissive

Keterangan

Opsi /permissive- ini didukung di Visual Studio 2017 dan yang lebih baru. /permissive didukung di Visual Studio 2019 versi 16.8 dan yang lebih baru.

Anda dapat menggunakan /permissive- opsi pengkompilasi untuk menentukan perilaku pengkompilasi yang sesuai dengan standar. Opsi ini menonaktifkan perilaku permisif, dan mengatur opsi pengkompilasi untuk kesuaian /Zc yang ketat. Dalam IDE, opsi ini juga membuat mesin IntelliSense menggaris bawahi kode yang tidak sesuai.

Opsi ini /permissive- menggunakan dukungan kesuaian dalam versi kompilator saat ini untuk menentukan konstruksi bahasa mana yang tidak sesuai. Opsi ini tidak menentukan apakah kode Anda sesuai dengan versi tertentu dari standar C++. Untuk mengaktifkan semua dukungan kompilator yang diimplementasikan untuk standar draf terbaru, gunakan /std:c++latest opsi . Untuk membatasi dukungan pengkompilasi ke standar C++20 yang saat ini diterapkan, gunakan opsi ./std:c++20 Untuk membatasi dukungan pengkompilasi ke standar C++17 yang saat ini diterapkan, gunakan opsi ./std:c++17 Untuk membatasi dukungan pengkompilasi agar lebih cocok dengan standar C++14, gunakan /std:c++14 opsi , yang merupakan default.

Opsi /permissive- ini secara implisit diatur oleh /std:c++latest opsi yang dimulai di Visual Studio 2019 versi 16.8, dan di versi 16.11 dengan /std:c++20 opsi . /permissive- diperlukan untuk dukungan Modul C++20. Mungkin kode Anda tidak memerlukan dukungan modul tetapi memerlukan fitur lain yang diaktifkan di bawah /std:c++20 atau /std:c++latest. Anda dapat secara eksplisit mengaktifkan dukungan ekstensi Microsoft dengan menggunakan /permissive opsi tanpa tanda hubung berikutnya. Opsi /permissive harus datang setelah opsi apa pun yang diatur secara implisit /permissive- .

Secara default, /permissive- opsi diatur dalam proyek baru yang dibuat oleh Visual Studio 2017 versi 15.5 dan versi yang lebih baru. Ini tidak diatur secara default di versi sebelumnya. Saat opsi diatur, pengkompilasi menghasilkan kesalahan diagnostik atau peringatan saat konstruksi bahasa non-standar terdeteksi dalam kode Anda. Konstruksi ini mencakup beberapa bug umum dalam kode pra-C++11.

Opsi /permissive- ini kompatibel dengan hampir semua file header dari Windows Kit terbaru, seperti Software Development Kit (SDK) atau Windows Driver Kit (WDK), mulai dari Windows Fall Creators SDK (10.0.16299.0). Versi SDK yang lebih lama mungkin gagal dikompilasi karena /permissive- berbagai alasan kesuaian kode sumber. Kompilator dan SDK dikirim pada garis waktu rilis yang berbeda, sehingga ada beberapa masalah yang tersisa. Untuk masalah file header tertentu, lihat Masalah header Windows di bawah ini.

Opsi /permissive- mengatur /Zc:referenceBindingopsi , /Zc:strictStrings, dan /Zc:rvalueCast untuk menyesuaikan perilaku. Opsi ini default ke perilaku yang tidak sesuai. Anda dapat meneruskan opsi tertentu /Zc setelah /permissive- pada baris perintah untuk mengambil alih perilaku ini.

Dalam versi pengkompilasi yang dimulai di Visual Studio 2017 versi 15.3, /permissive- opsi mengatur /Zc:ternary opsi . Kompilator juga mengimplementasikan lebih banyak persyaratan untuk pencarian nama dua fase. /permissive- Saat opsi diatur, pengkompilasi mengurai definisi templat fungsi dan kelas, dan mengidentifikasi nama dependen dan non-dependen yang digunakan dalam templat. Dalam rilis ini, hanya analisis dependensi nama yang dilakukan.

Pada Visual Studio 2022 Update 17.6, /permissive- opsi mengatur /Zc:lambda opsi dan /Zc:externConstexpr . Di versi sebelumnya, /permissive- tidak mengatur salah satu.

Ekstensi khusus lingkungan dan area bahasa yang ditinggalkan standar hingga implementasi tidak terpengaruh oleh /permissive-. Misalnya, konvensi khusus Microsoft __declspec, konvensi panggilan dan kata kunci penanganan pengecualian terstruktur, dan arahan atau atribut khusus pragma kompilator tidak ditandai oleh pengkompilasi dalam /permissive- mode.

Pengkompilasi MSVC di versi Visual Studio 2017 yang lebih lama tidak mendukung semua kode yang sesuai dengan standar C++11, C++14, atau C++17. Bergantung pada versi Visual Studio, /permissive- opsi mungkin tidak mendeteksi masalah dalam beberapa aspek pencarian nama dua fase, mengikat referensi non-const ke sementara, memperlakukan init salinan sebagai init langsung, memungkinkan beberapa konversi yang ditentukan pengguna dalam inisialisasi, atau token alternatif untuk operator logis, dan area konformasi lain yang tidak didukung. Untuk informasi selengkapnya tentang masalah kesuaian di Visual C++, lihat Perilaku Nonstandar. Untuk mendapatkan hasil maksimal dari /permissive-, perbarui Visual Studio ke versi terbaru.

Cara memperbaiki kode Anda

Berikut adalah beberapa contoh kode yang terdeteksi tidak sesuai saat Anda menggunakan /permissive-, bersama dengan cara yang disarankan untuk memperbaiki masalah.

Gunakan default sebagai pengidentifikasi dalam kode asli

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

Mencari anggota di basis dependen

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

Penggunaan nama yang memenuhi syarat dalam deklarasi anggota

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

Menginisialisasi beberapa anggota serikat dalam penginisialisasi anggota

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

Aturan pencarian nama teman tersembunyi

Deklarasi di luar kelas dapat membuat teman tersembunyi terlihat:

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

Penggunaan literal nullptr dapat mencegah pencarian dependen argumen:

// 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.
}

Anda dapat mengaktifkan aturan pencarian nama teman tersembunyi secara /permissive independen dengan menggunakan /Zc:hiddenFriend. Jika Anda menginginkan perilaku warisan untuk pencarian nama teman tersembunyi, tetapi jika tidak ingin /permissive- perilaku, gunakan /Zc:hiddenFriend- opsi .

Menggunakan enum terlingkup dalam batas array

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.

Gunakan untuk masing-masing dalam kode asli

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)
    {
        // ...
    }
}

Penggunaan atribut ATL

Atribut ATL khusus Microsoft dapat menyebabkan masalah di bawah /permissive-:

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

Anda dapat memperbaiki masalah dengan menggunakan formulir sebagai gantinya __declspec :

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

Contoh yang lebih kompleks:

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

Resolusi memerlukan langkah-langkah build tambahan. Dalam hal ini, buat file 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()
};

Argumen operator bersyarat ambigu

Dalam versi pengompilasi sebelum Visual Studio 2017 versi 15.3, pengompilasi menerima argumen ke operator bersyarat (atau operator terner) ?: yang dianggap ambigu oleh Standar. Dalam /permissive- mode, kompilator sekarang mengeluarkan satu atau beberapa diagnostik dalam kasus yang dikompilasi tanpa diagnostik di versi sebelumnya.

Kesalahan umum yang mungkin diakibatkan oleh perubahan ini meliputi:

  • 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'

Pola kode umum yang dapat menyebabkan masalah ini adalah ketika beberapa kelas C menyediakan konstruktor non-eksplisit dari jenis T lain dan operator konversi non-eksplisit untuk mengetik T. Konversi argumen kedua ke jenis argumen ketiga adalah konversi yang valid. Begitu juga konversi argumen ketiga ke jenis argumen kedua. Karena keduanya valid, itu ambigu sesuai dengan standar.

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

Ada pengecualian penting untuk pola umum ini ketika T mewakili salah satu jenis string yang dihentikan null (misalnya, , const char *, const char16_t *dan sebagainya) dan argumen aktual untuk ?: adalah string harfiah dari jenis yang sesuai. C++17 telah mengubah semantik dari C++14. Akibatnya, kode dalam contoh 2 diterima di bawah /std:c++14 dan ditolak di bawah /std:c++17 atau yang lebih baru saat /Zc:ternary atau /permissive- digunakan.

// 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);

Anda mungkin juga melihat kesalahan dalam operator bersyarah dengan satu argumen jenis void. Kasus ini mungkin umum dalam makro seperti 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__))

Anda mungkin juga melihat kesalahan dalam pemrograman metaprogram templat, di mana jenis hasil operator bersyar dapat berubah di bawah /Zc:ternary dan /permissive-. Salah satu cara untuk mengatasi masalah ini adalah dengan menggunakan std::remove_reference pada jenis yang dihasilkan.

// 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

Pencarian nama dua fase

/permissive- Saat opsi diatur, pengkompilasi mengurai definisi templat fungsi dan kelas, mengidentifikasi nama dependen dan non-dependen yang digunakan dalam templat sebagaimana diperlukan untuk pencarian nama dua fase. Di Visual Studio 2017 versi 15.3, analisis dependensi nama dilakukan. Secara khusus, nama non-dependen yang tidak dideklarasikan dalam konteks definisi templat menyebabkan pesan diagnostik sebagaimana diperlukan oleh standar ISO C++. Di Visual Studio 2017 versi 15.7, pengikatan nama non-dependen yang memerlukan pencarian tergantung argumen dalam konteks definisi juga dilakukan.

// 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();
}

Jika Anda menginginkan perilaku warisan untuk pencarian dua fase, tetapi jika tidak ingin /permissive- perilaku, tambahkan /Zc:twoPhase- opsi .

Masalah header Windows

Opsi /permissive- ini terlalu ketat untuk versi Windows Kit sebelum Windows Fall Creators Update SDK (10.0.16299.0), atau Windows Driver Kit (WDK) versi 1709. Sebaiknya perbarui ke versi terbaru Windows Kits untuk digunakan /permissive- dalam kode driver Windows atau perangkat Anda.

File header tertentu di Windows April 2018 Update SDK (10.0.17134.0), Windows Fall Creators Update SDK (10.0.16299.0), atau Windows Driver Kit (WDK) 1709, masih memiliki masalah yang membuatnya tidak kompatibel dengan penggunaan /permissive-. Untuk mengatasi masalah ini, kami sarankan Anda membatasi penggunaan header ini hanya untuk file kode sumber yang memerlukannya, dan menghapus /permissive- opsi saat Anda mengkompilasi file kode sumber tertentu tersebut.

Header WinRT WRL yang dirilis di Windows April 2018 Update SDK (10.0.17134.0) tidak bersih dengan /permissive-. Untuk mengatasi masalah ini, jangan gunakan /permissive-, atau gunakan /permissive-/Zc:twoPhase- saat Anda bekerja dengan header ini:

  • Masalah dalam 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
    
  • Masalah dalam 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'
    

Header Mode Pengguna yang dirilis di Windows April 2018 Update SDK (10.0.17134.0) tidak bersih dengan /permissive-. Untuk mengatasi masalah ini, jangan gunakan /permissive- saat bekerja dengan header ini:

  • Masalah dalam 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
    
  • Masalah dalam 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
    
  • Masalah dalam 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'
    

Masalah ini khusus untuk header Mode Pengguna di Windows Fall Creators Update SDK (10.0.16299.0):

  • Masalah dalam um/Query.h

    Saat Anda menggunakan /permissive- sakelar kompilator, tagRESTRICTION struktur tidak dikompilasi karena case(RTOr) anggota 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;
    };
    

    Untuk mengatasi masalah ini, kompilasi file yang disertakan Query.h/permissive- tanpa opsi .

  • Masalah dalam um/cellularapi_oem.h

    Saat Anda menggunakan /permissive- sakelar kompilator, deklarasi enum UICCDATASTOREACCESSMODE maju menyebabkan peringatan:

    typedef enum UICCDATASTOREACCESSMODE UICCDATASTOREACCESSMODE; // C4471
    

    Deklarasi maju dari yang tidak dilingkupkan enum adalah ekstensi Microsoft. Untuk mengatasi masalah ini, kompilasi file yang disertakan cellularapi_oem.h tanpa /permissive- opsi , atau gunakan /wd opsi untuk membungkam peringatan C4471.

  • Masalah dalam um/omscript.h

    Di C++03, konversi dari string literal ke BSTR (yang merupakan typedef ke wchar_t *) tidak digunakan lagi tetapi diizinkan. Di C++11, konversi tidak lagi diizinkan.

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

    Untuk mengatasi masalah ini, kompilasi file yang menyertakan omscript.h tanpa /permissive- opsi , atau gunakan /Zc:strictStrings- sebagai gantinya.

Untuk mengatur opsi pengkompilasi ini di lingkungan pengembangan Visual Studio

Di Visual Studio 2017 versi 15.5 dan versi yang lebih baru, gunakan prosedur ini:

  1. Buka kotak dialog Halaman Properti proyek Anda.

  2. Pilih halaman properti Properti>Konfigurasi C/C++>Language.

  3. Ubah nilai properti Mode kesuaian menjadi Ya (/permisif-). Pilih OK atau Terapkan untuk menyimpan perubahan Anda.

Dalam versi sebelum Visual Studio 2017 versi 15.5, gunakan prosedur ini:

  1. Buka kotak dialog Halaman Properti proyek Anda.

  2. Pilih halaman properti Properti>Konfigurasi C/C++>Baris Perintah.

  3. Masukkan opsi /permissive- compiler di kotak Opsi Tambahan. Pilih OK atau Terapkan untuk menyimpan perubahan Anda.

Untuk mengatur opsi pengkompilasi ini secara terprogram

Baca juga

Opsi Pengkompilasi MSVC
Sintaks Baris Perintah Pengkompilasi MSVC