/permissive- (Соответствие стандартам)

Укажите режим соответствия стандартам компилятору. Используйте этот параметр, чтобы определить и устранить проблемы соответствия в коде, чтобы сделать его более правильным и более переносимым.

Синтаксис

/permissive-
/permissive

Замечания

Этот /permissive- параметр поддерживается в Visual Studio 2017 и более поздних версиях. /permissive поддерживается в Visual Studio 2019 версии 16.8 и более поздних версий.

Параметр компилятора /permissive- можно использовать для указания поведения компилятора, соответствующего стандартам. Этот параметр отключает разрешительное поведение и задает /Zc параметры компилятора для строгого соответствия. В интегрированной среде разработки этот параметр также делает подсистему IntelliSense подчеркиванием несоответствующего кода.

Параметр /permissive- использует поддержку соответствия в текущей версии компилятора, чтобы определить, какие конструкции языка не соответствуют. Параметр не определяет, соответствует ли код определенной версии стандарта C++. Чтобы включить все реализованные поддержку компилятора для последнего стандарта черновика, используйте этот /std:c++latest параметр. Чтобы ограничить поддержку компилятора в настоящее время реализованным стандартом C++20, используйте этот /std:c++20 параметр. Чтобы ограничить поддержку компилятора в настоящее время реализованным стандартом C++17, используйте этот /std:c++17 параметр. Чтобы ограничить поддержку компилятора более тесно соответствовать стандарту C++14, используйте /std:c++14 параметр, который является значением по умолчанию.

Параметр /permissive- неявно устанавливается параметром /std:c++latest , начиная с Visual Studio 2019 версии 16.8 и в версии 16.11 по параметру /std:c++20 . /permissive- требуется для поддержки модулей C++20. Возможно, код не нуждается в поддержке модулей, но требует других функций, включенных в /std:c++20 или /std:c++latest. Вы можете явно включить поддержку расширений Майкрософт с помощью /permissive параметра без конечных тире. Этот /permissive параметр должен поступать после любого параметра, который задает /permissive- неявно.

По умолчанию параметр /permissive- устанавливается в новых проектах, созданных Visual Studio 2017 версии 15.5 и более поздних версий. Он не задан по умолчанию в более ранних версиях. При установке параметра компилятор создает диагностические ошибки или предупреждения при обнаружении в коде нестандартных конструкций языка. Эти конструкции включают некоторые распространенные ошибки в коде до C++11.

Этот /permissive- параметр совместим с почти всеми файлами заголовков из последних комплектов Windows, таких как пакет SDK для программного обеспечения или комплект драйверов Windows (WDK), начиная с пакета SDK для Windows Fall Creators (10.0.16299.0.0). Более старые версии пакета SDK могут не компилироваться по /permissive- различным причинам соответствия исходного кода. Компилятор и пакеты SDK отправляются в разных временная шкала выпуска, поэтому существуют некоторые оставшиеся проблемы. Сведения о конкретных проблемах с файлами заголовков см. в разделе о проблемах с заголовками Windows ниже.

Параметр /permissive- задает /Zc:referenceBinding/Zc:strictStringsпараметры и /Zc:rvalueCast параметры для соответствия поведению. Эти параметры по умолчанию не соответствуют поведению. Вы можете передать определенные /Zc параметры после того, как /permissive- в командной строке переопределить это поведение.

В версиях компилятора, начиная с Visual Studio 2017 версии 15.3, /permissive- параметр задает /Zc:ternary этот параметр. Компилятор также реализует больше требований для двухфазного поиска имен. /permissive- При установке параметра компилятор анализирует определения функций и шаблонов классов и определяет зависимые и независимые имена, используемые в шаблонах. В этом выпуске выполняется только анализ зависимостей имен.

По состоянию на Visual Studio 2022 с обновлением 17.6 /permissive- параметр задает /Zc:lambda и /Zc:externConstexpr параметры. В предыдущих версиях /permissive- не задано ни одно.

Расширения для конкретной среды и языковые области, которые стандарт не влияет /permissive-на реализацию. Например, определенные корпорацией Майкрософт__declspec, соглашения о вызовах и структурированной обработке исключений ключевое слово, а директивы или атрибуты, относящиеся pragma к компилятору, не помечены в /permissive- режиме компилятора.

Компилятор MSVC в более ранних версиях Visual Studio 2017 не поддерживает код, соответствующий стандартам C++11, C++14 или C++17. В зависимости от версии Visual Studio /permissive- параметр может не обнаруживать проблемы в некоторых аспектах подстановки двухфазного имени, привязывая неконстантную ссылку на временную, обрабатывая инициализацию копирования как прямую инициализацию, позволяя несколько определяемых пользователем преобразований в инициализации или альтернативные маркеры для логических операторов и другие не поддерживаемые области соответствия. Дополнительные сведения о вопросах соответствия в 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

Атрибуты 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 компилятор принял аргументы условному оператору (или ternary оператору), ?: которые считаются неоднозначными по стандарту. В /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/std:c++17 в случае или более поздней версии при /Zc:ternary использовании /permissive- .

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

Поиск двухфазного имени

/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

Этот /permissive- параметр слишком строгий для версий комплектов Windows до пакета SDK для Windows Fall Creators Update (10.0.16299.0) или комплект драйверов Windows (WDK) версии 1709. Рекомендуется обновить до последних версий комплектов Windows, используемых /permissive- в коде драйвера Windows или устройства.

Некоторые файлы заголовков в пакете SDK для обновления Windows за апрель 2018 г. (10.0.17134.0), пакет SDK для Windows Fall Creators Update (10.0.16299.0) или пакет драйверов Windows (WDK) 1709 по-прежнему имеют проблемы, которые делают их несовместимыми с использованием /permissive-. Чтобы обойти эти проблемы, рекомендуется ограничить использование этих заголовков только теми файлами исходного кода, которые требуют их, и удалить /permissive- параметр при компиляции этих конкретных файлов исходного кода.

Эти заголовки WRL WinRT, выпущенные в пакете SDK для обновления Windows за апрель 2018 г. (10.0.17134.0), не являются чистыми./permissive- Чтобы обойти эти проблемы, не используйте или не используйте /permissive-/permissive- их /Zc:twoPhase- при работе с этими заголовками:

  • Проблемы в 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'
    

Эти заголовки режима пользователя, выпущенные в пакете SDK для обновления Windows за апрель 2018 г. (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'
    

Эти проблемы относятся к заголовкам режима пользователя в пакете SDK для Windows Fall Creators Update (10.0.16299.0):

  • Проблема в um/Query.h

    При использовании коммутатора tagRESTRICTION компилятора /permissive- структура не компилируется из-за 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;
    };
    

    Чтобы устранить эту проблему, скомпилируйте файлы, которые включаются Query.h без /permissive- параметра.

  • Проблема в um/cellularapi_oem.h

    При использовании переключателя компилятора /permissive- объявление пересылки enum UICCDATASTOREACCESSMODE вызывает предупреждение:

    typedef enum UICCDATASTOREACCESSMODE UICCDATASTOREACCESSMODE; // C4471
    

    Объявление пересылки un область d enum является расширением Майкрософт. Чтобы устранить эту проблему, скомпилируйте файлы, включаемые cellularapi_oem.h без /permissive- параметра, или используйте /wd параметр для молчания предупреждения C4471.

  • Проблема в um/omscript.h

    В C++03 преобразование из строкового литерала BSTR в (то есть типdef в 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++>Language.

  3. Измените значение свойства режима соответствия на "Да" (/permissive-). Нажмите кнопку "ОК" или "Применить", чтобы сохранить изменения.

В версиях до Visual Studio 2017 версии 15.5 используйте эту процедуру:

  1. Откройте диалоговое окно страниц свойств проекта.

  2. Перейдите на страницу свойств Свойства конфигурации>C/C++>Командная строка.

  3. Введите параметр компилятора /permissive- в поле "Дополнительные параметры". Нажмите кнопку "ОК" или "Применить", чтобы сохранить изменения.

Установка данного параметра компилятора программным способом

См. также

Параметры компилятора MSVC
Синтаксис командной строки компилятора MSVC