/permissive- (conformidad con los estándares)

Especifique el modo de conformidad de estándares para el compilador. Use esta opción para ayudarle a identificar y corregir incidencias de conformidad en el código, para que sea más correcto y más portable.

Sintaxis

/permissive-
/permissive

Comentarios

La opción /permissive- se admite en Visual Studio 2017 y versiones posteriores. /permissive se admite en Visual Studio 2019, versión 16.8 y posteriores.

Puede usar la opción del compilador /permissive- para especificar el comportamiento del compilador conforme a los estándares. Esta opción deshabilita los comportamientos permisivos y establece las opciones del compilador /Zc para una conformidad estricta. En el IDE (entorno de desarrollo integrado), esta opción también hace que el motor de IntelliSense subraye el código no conforme.

La opción /permissive- usa la compatibilidad de conformidad en la versión actual del compilador para determinar qué construcciones de lenguaje no son conformes. La opción no determina si el código se conforma a una versión específica del estándar de C++. Para habilitar toda la compatibilidad del compilador implementada con el último estándar de borrador, use la opción /std:c++latest. Para restringir la compatibilidad del compilador con el estándar de C++20 implementado actualmente, use la opción /std:c++20. Para restringir la compatibilidad del compilador con el estándar de C++17 implementado actualmente, use la opción /std:c++17. Para restringir la compatibilidad del compilador para que coincida más estrechamente con el estándar de C++14, use la opción /std:c++14, que es el valor predeterminado.

La opción /permissive- se establece implícitamente mediante la opción /std:c++latest a partir de Visual Studio 2019, versión 16.8, y en la versión 16.11 por la opción /std:c++20. /permissive- es necesario para la compatibilidad con módulos de C++20. Quizás el código no necesite compatibilidad con módulos, pero requiera otras características habilitadas en /std:c++20 o /std:c++latest. Puede habilitar explícitamente la compatibilidad con la extensión de Microsoft mediante la opción /permissive sin el guión final. La opción /permissive debe aparecer después de cualquier opción que establezca /permissive- implícitamente.

De forma predeterminada, la opción /permissive- se establece en nuevos proyectos creados por Visual Studio 2017, versión 15.5 y posteriores. No se establece de forma predeterminada en versiones anteriores. Cuando se establece la opción, el compilador genera errores de diagnóstico o advertencias cuando se detectan construcciones de lenguaje no estándar en el código. Estas construcciones incluyen algunos errores comunes en el código anterior a C++11.

La opción /permissive- es compatible con casi todos los archivos de encabezado de los kits de Windows más recientes, como el kit de desarrollo de software (SDK) o el kit de controladores de Windows (WDK), a partir del SDK de Windows Fall Creators (10.0.16299.0). Es posible que las versiones anteriores del SDK no se compilen en /permissive- por diversos motivos de conformidad del código fuente. El compilador y los SDK se envían en diferentes escalas de tiempo de versión, por lo que hay algunas incidencias restantes. Sobre incidencias específicas de archivos de encabezado, consulte Problemas de encabezado de Windows a continuación.

La opción /permissive- establece las opciones /Zc:referenceBinding, /Zc:strictStrings y /Zc:rvalueCast para conformar el comportamiento. Estas opciones se ajustan de forma predeterminada al comportamiento no conforme. Puede pasar opciones específicas /Zc después de /permissive- en la línea de comandos para invalidar este comportamiento.

En versiones del compilador a partir de Visual Studio 2017 versión 15.3, la opción /permissive- establece la opción /Zc:ternary. El compilador también implementa más requisitos para la búsqueda de nombres en dos fases. Cuando se establece la opción /permissive-, el compilador analiza las definiciones de plantilla de clase y función, e identifica los nombres dependientes y no dependientes usados en las plantillas. En esta versión, solo se realiza el análisis de dependencias de nombres.

A partir de Visual Studio 2022 Update 17.6, la /permissive- opción establece las /Zc:lambda opciones y /Zc:externConstexpr . En versiones anteriores, /permissive- no se estableció ninguna.

Las extensiones y áreas de lenguaje específicas del entorno que el estándar deja hasta la implementación no se ven afectadas por /permissive-. Por ejemplo, las __declspec específicas de Microsoft, las palabras clave de manejo de convención de llamada excepción estructurada, y las directivas pragma específicas de compilador no están marcados por el compilador en modo /permissive-.

El compilador de MSVC en versiones anteriores de Visual Studio 2017 no admite todo el código conforme a estándares de C++11, C++14 o C++17. En función de la versión de Visual Studio, la opción /permissive- puede no detectar incidencias en algunos aspectos de la búsqueda de nombres en dos fases, enlazando una referencia no-const a una referencia temporal, tratando la init de copia como inicialización directa, permitiendo varias conversiones definidas por el usuario en la inicialización, o tokens alternativos para operadores lógicos y otras áreas de conformidad no admitidas. Para obtener más información sobre los problemas de conformidad de Visual C++, vea Nonstandard Behavior. Para sacar el máximo partido de /permissive-, actualice Visual Studio a la versión más reciente.

Cómo corregir el código

Estos son algunos ejemplos de código que se detecta como no conforme al usar /permissive-, junto con formas sugeridas de corregir las incidencias.

Uso de default como identificador en código nativo

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

Búsqueda de miembros en la base dependiente

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

Uso de nombres calificados en declaraciones de miembro

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

Inicialización de varios miembros de unión en un inicializador de miembro

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

Reglas de búsqueda de nombres de confianza ocultas

Una declaración fuera de una clase puede hacer que un amigo oculto sea visible:

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

El uso del literal nullptr puede impedir la búsqueda dependiente del argumento:

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

Puede habilitar las reglas de búsqueda de nombres de amigo oculto independientemente de /permissive mediante /Zc:hiddenFriend. Si desea un comportamiento heredado para la búsqueda de nombres de amigo oculto, pero de lo contrario desea comportamiento /permissive-, use la opción /Zc:hiddenFriend-.

Uso de enumeraciones con ámbito en los límites de matriz

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.

Uso para cada uno en código nativo

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

Uso de atributos ATL (Active Template Library)

Los atributos ATL específicos de Microsoft pueden causar incidencias en /permissive-:

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

Puede corregir la incidencia mediante el formulario __declspec en su lugar:

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

Ejemplo más complejo:

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

La resolución requiere pasos de compilación adicionales. En este caso, cree un archivo 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()
};

Argumentos de operador condicional ambiguos

En versiones del compilador anteriores a la versión 15.3 de Visual Studio 2017, el compilador aceptó argumentos para el operador condicional (o operador ternario) ?: que el estándar considera ambiguo. En el modo /permissive-, el compilador ahora emite uno o varios diagnósticos en los casos que se compilan sin diagnóstico en versiones anteriores.

Entre los errores comunes que pueden producirse a partir de este cambio se incluyen:

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

Un patrón de código típico que puede causar esta incidencia es cuando alguna clase C proporciona un constructor no explícito de otro tipo T y un operador de conversión no explícito al tipo T. La conversión del segundo argumento al tipo del tercer argumento es una conversión válida. Así es también la conversión del tercer argumento al tipo del segundo argumento. Dado que ambos son válidos, es ambiguo según el estándar.

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

Hay una excepción importante a este patrón común cuando T representa uno de los tipos de cadena terminados en NULL (por ejemplo, const char *, const char16_t *, etc.) y el argumento real de ?: es un literal de cadena de tipo correspondiente. C++17 ha cambiado la semántica de C++14. Como resultado, el código del ejemplo 2 se acepta bajo /std:c++14 y se rechaza bajo /std:c++17 o posterior cuando se usa /Zc:ternary o /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);

También puede ver errores en operadores condicionales con un argumento de tipo void. Este caso puede ser común en macros similares a 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__))

También puede ver errores en la metaprogramación de plantillas, donde los tipos de resultados de operador condicional pueden cambiar bajo /Zc:ternary y /permissive-. Una manera de resolver esta incidencia es usar std::remove_reference en el tipo resultante.

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

Búsqueda de nombres en dos fases

Cuando se establece la opción /permissive-, el compilador analiza las definiciones de plantilla de clase y función, identificando los nombres dependientes y no dependientes usados en las plantillas según sea necesario para la búsqueda de nombres en dos fases. En Visual Studio 2017, versión 15.3, se realiza el análisis de dependencia de nombres. En concreto, los nombres no dependientes que no se declaran en el contexto de una definición de plantilla provocan un mensaje de diagnóstico según lo requieran los estándares ISO de C++. En la versión 15.7 de Visual Studio 2017, también se realiza el enlace de nombres no dependientes que requieren una búsqueda dependiente del argumento en el contexto de definición.

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

Si desea un comportamiento heredado para la búsqueda en dos fases, pero de lo contrario quiere un comportamiento /permissive-, agregue la opción /Zc:twoPhase-.

Incidencias de encabezado de Windows

La opción /permissive- es demasiado estricta para las versiones de kits de Windows antes del SDK de actualización de Windows Fall Creators (10.0.16299.0) o Windows Driver Kit (WDK) versión 1709. Se recomienda actualizar a las versiones más recientes de los kits de Windows para usar /permissive- en el código del controlador de dispositivo o Windows.

Algunos archivos de encabezado en el SDK de actualización de abril de 2018 de Windows (10.0.17134.0), el SDK de actualización de Windows Fall Creators (10.0.16299.0) o el kit de controladores de Windows (WDK) 1709, siguen teniendo incidencias que los hacen incompatibles con el uso de /permissive-. Para solucionar estas incidencias se recomienda restringir el uso de estos encabezados solo a los archivos de código fuente que los requieran y quitar la opción /permissive- al compilar esos archivos de código fuente específicos.

Estos encabezados WRL de WinRT publicados en el SDK de actualización de abril de 2018 de Windows (10.0.17134.0) no están limpios con /permissive-. Para solucionar estas incidencias, no use /permissive- o use /permissive- con /Zc:twoPhase- cuando trabaje con estos encabezados:

  • Incidencias en 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
    
  • Incidencia en 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'
    

Estos encabezados de modo de usuario publicados en el SDK de actualización de abril de 2018 de Windows (10.0.17134.0) no están limpios con /permissive-. Para solucionar estas incidencias, no use /permissive- al trabajar con estos encabezados:

  • Incidencias en 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
    
  • Incidencia en 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
    
  • Incidencias en 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'
    

Estas incidencias son específicas de los encabezados del modo de usuario en el SDK de la actualización de Windows Fall Creators (10.0.16299.0):

  • Incidencia en um/Query.h

    Cuando se usa el modificador de compilador /permissive-, la estructura tagRESTRICTION no se compila debido al case(RTOr) miembro 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;
    };
    

    Para solucionar esta incidencia, compile archivos que incluyan Query.h sin la opción /permissive-.

  • Incidencia en um/cellularapi_oem.h

    Cuando use el modificador del compilador /permissive-, la declaración de reenvío de enum UICCDATASTOREACCESSMODE provoca una advertencia:

    typedef enum UICCDATASTOREACCESSMODE UICCDATASTOREACCESSMODE; // C4471
    

    La declaración de reenvío de un enum no limitado es una extensión de Microsoft. Para solventar esta incidencia, compile archivos que incluyan cellularapi_oem.h sin la opción /permissive- o use la opción /wd para silenciar la advertencia C4471.

  • Incidencia en um/omscript.h

    En C++03, una conversión desde un literal de cadena a BSTR (que es una typedef a wchar_t *) está en desuso, pero se permite. En C++11 ya no se permite la conversión.

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

    Para solventar este problema, compile archivos que incluyan omscript.h sin la opción /permissive- o usen /Zc:strictStrings- en su lugar.

Para establecer esta opción del compilador en el entorno de desarrollo de Visual Studio

En Visual Studio 2017 versión 15.5 y versiones posteriores, use este procedimiento:

  1. Abra el cuadro de diálogo Páginas de propiedades del proyecto.

  2. Seleccione la página de propiedades Propiedades de configuración>C/C++>Lenguaje.

  3. Cambie el valor de la propiedad Modo de conformidad a Sí (/permissive-). Haga clic en Aceptar o en Aplicar para guardar los cambios.

En versiones anteriores a Visual Studio 2017 versión 15.5, use este procedimiento:

  1. Abra el cuadro de diálogo Páginas de propiedades del proyecto.

  2. Seleccione la página de propiedades Propiedades de configuración>C/C++>Línea de comandos.

  3. Escriba la opción del compilador /permissive- en el cuadro Opciones adicionales. Haga clic en Aceptar o en Aplicar para guardar los cambios.

Para establecer esta opción del compilador mediante programación

Consulte también

Opciones del compilador de MSVC
Sintaxis de la línea de comandos del compilador MSVC