/permissive- (conformidade com padrões)

Especifique o modo de conformidade com padrões para o compilador. Use essa opção para ajudar a identificar e corrigir problemas de conformidade no código, para deixá-lo mais correto e mais portátil.

Sintaxe

/permissive-
/permissive

Comentários

A opção /permissive- tem suporte no Visual Studio 2017 e posterior. /permissive tem suporte no Visual Studio 2019 versão 16.8 e posteriores.

É possível usar a opção do compilador /permissive- para especificar o comportamento do compilador em termos da conformidade com padrões. Essa opção desabilita comportamentos permissivos e define as opções do compilador /Zc para que haja conformidade estrita. No IDE, essa opção também faz com que o mecanismo IntelliSense sublinhe o código não compatível.

A opção /permissive- usa o suporte de conformidade na versão atual do compilador para determinar quais constructos de idioma não estão em conformidade. A opção não determina se o código mantém a conformidade com uma versão específica do padrão C++. Para habilitar todo o suporte do compilador implementado para o padrão redigido mais recentemente, use a opção /std:c++latest. Para restringir o suporte do compilador ao padrão C++20 implementado atualmente, use a opção /std:c++20. Para restringir o suporte do compilador ao padrão C++17 implementado atualmente, use a opção /std:c++17. Para restringir o suporte do compilador de maneira a corresponder mais de perto ao padrão C++14, use a opção /std:c++14, que é o padrão.

A opção /permissive- é definida implicitamente pela opção /std:c++latest, começando no Visual Studio 2019 versão 16.8, e na versão 16.11 pela opção /std:c++20. /permissive- é necessário para dar suporte aos Módulos C++20. Talvez seu código não precise de suporte para módulos, mas exija outros recursos habilitados em /std:c++20 ou /std:c++latest. Você pode habilitar explicitamente o suporte à extensão da Microsoft usando a opção /permissive sem o traço à direita. A opção /permissive deve vir após qualquer opção que defina /permissive- implicitamente.

Por padrão, a opção /permissive- é definida em novos projetos criados pelo Visual Studio 2017 versão 15.5 e versões posteriores. Ela não é definida por padrão nas versões anteriores. Quando a opção é definida, o compilador gera erros de diagnóstico ou avisos quando constructos de linguagem não padrão são detectados no código. Esses constructos incluem alguns bugs comuns no código anterior ao C++11.

A opção /permissive- é compatível com quase todos os arquivos de cabeçalho dos kits do Windows mais recentes, como o SDK (Software Development Kit) e o WDK (Windows Driver Kit), começando no SDK do Windows Fall Creators (10.0.16299.0). A compilação de versões mais antigas do SDK pode falhar com /permissive- por vários motivos de conformidade do código-fonte. O compilador e os SDKs são fornecidos seguindo diferentes linhas do tempo de lançamento, portanto, há alguns problemas restantes. Para saber mais sobre problemas específicos do arquivo de cabeçalho, confira os Problemas de cabeçalho do Windows abaixo.

A opção /permissive- define as opções /Zc:referenceBinding, /Zc:strictStrings e /Zc:rvalueCast para um comportamento compatível. O padrão dessas opções é o comportamento não compatível. Você pode passar opções /Zc específicas depois de /permissive- na linha de comando para substituir esse comportamento.

Em versões do compilador começando no Visual Studio 2017 versão 15.3, a opção /permissive- define a opção /Zc:ternary. O compilador também implementa mais dos requisitos de pesquisa de nome em duas fases. Quando a opção /permissive- é definida, o compilador analisa definições de modelo de função e classe e identifica nomes dependentes e não dependentes usados nos modelos. Nesta versão, somente a análise de dependência de nomes é executada.

A partir da Atualização 17.6 do Visual Studio 2022, a /permissive- opção define as /Zc:lambda opções e /Zc:externConstexpr . Nas versões anteriores, /permissive- não definia nenhuma das duas.

Extensões específicas do ambiente e áreas de linguagem que o padrão relega à implementação não são afetadas por /permissive-. Por exemplo, o __declspec específico da Microsoft, as palavras-chave de tratamento de exceções estruturadas e convenção de chamada e as diretivas ou atributos pragma específicos do compilador não são sinalizados pelo compilador no modo /permissive-.

O compilador do MSVC em versões anteriores do Visual Studio 2017 não dá suporte a todos os códigos em conformidade com os padrões C++11, C++14 ou C++17. Dependendo da versão do Visual Studio, a opção /permissive- pode não detectar problemas em alguns aspectos da pesquisa de nome de duas fases, como associar uma referência não constante a uma temporária, tratar a inicialização de cópia como inicialização direta, permitir várias conversões definidas pelo usuário na inicialização ou usar tokens alternativos para operadores lógicos, bem como outras áreas de conformidade sem suporte. Para obter mais informações sobre problemas de conformidade no Visual C++, confira Comportamento fora do padrão. Para aproveitar ao máximo o /permissive-, atualize o Visual Studio para a versão mais recente.

Como corrigir o código

Aqui, temos alguns exemplos de código apontado como fora de conformidade quando você usa /permissive-, bem como maneiras sugeridas de corrigir os problemas.

Usar default como um identificador no código nativo

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

Pesquisar membros na base dependente

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 nomes qualificados em declarações de membro

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

Inicializar vários membros de união em um inicializador de membro

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

Regras de pesquisa de nome de amigo oculto

Uma declaração fora de uma classe pode tornar um amigo oculto visível:

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

O uso do literal nullptr pode impedir a pesquisa dependente de argumentos:

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

Você pode habilitar as regras de pesquisa de nome de amigo oculto independentemente de /permissive usando /Zc:hiddenFriend. Se você quiser o comportamento herdado para a pesquisa de nome de amigo oculto, mas nos outros casos quiser o comportamento /permissive-, use a opção /Zc:hiddenFriend-.

Usar enumerações com escopo em limites da 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.

Usar for each no 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 de ATL

Atributos de ATL específicos da Microsoft podem causar problemas em /permissive-:

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

Corrija o problema usando a forma __declspec:

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

Um exemplo mais complexo:

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

A resolução requer etapas de build extra. Nesse caso, crie um arquivo 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 ambíguos de operador condicional

Em versões do compilador anteriores ao Visual Studio 2017 versão 15.3, o compilador aceitava argumentos para o operador condicional (ou operador ternário) ?: considerados ambíguos pelo padrão. No modo /permissive-, o compilador emite um ou mais diagnósticos em casos que eram compilados sem diagnóstico em versões anteriores.

Erros comuns que podem decorrer dessa alteração incluem:

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

Um padrão de código típico que pode causar esse problema é quando alguma classe C fornece um construtor não explícito de outro tipo T e um operador de conversão não explícito para o tipo T. A conversão do segundo argumento para o tipo do terceiro argumento é uma conversão válida. Assim como a conversão do terceiro argumento para o tipo do segundo argumento. Como ambas são válidas, isso é ambíguo de acordo com o padrão.

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

Há uma exceção importante para esse padrão comum, em que T representa um dos tipos de cadeia de caracteres terminadas em nulo (por exemplo, const char *, const char16_t * e assim por diante) e o argumento real para ?: é um literal de cadeia de caracteres do tipo correspondente. O C++17 alterou a semântica do C++14. Como resultado, o código no exemplo 2 é aceito em /std:c++14 e rejeitado em /std:c++17 ou posterior quando /Zc:ternary ou /permissive- é usado.

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

Você também poderá ver erros em operadores condicionais com um argumento do tipo void. Esse caso pode ser comum em macros semelhantes 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__))

Você também poderá ver erros na metaprogramação do modelo, em que os tipos de resultado do operador condicional podem mudar em /Zc:ternary e /permissive-. Uma maneira de resolver esse problema é usar std::remove_reference no 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

Pesquisa de nome de duas fases

Quando a opção /permissive- é definida, o compilador analisa definições de modelo de classe e função, identificando os nomes dependentes e não dependentes usados nos modelos conforme necessário para a pesquisa de nome de duas fases. No Visual Studio 2017 versão 15.3, a análise de dependência de nome é executada. Em particular, nomes não dependentes que não são declarados no contexto de uma definição de modelo causam uma mensagem de diagnóstico, conforme exigido pelos padrões ISO do C++. No Visual Studio 2017 versão 15.7, a associação de nomes não dependentes que exigem a pesquisa dependente de argumento no contexto da definição também é feita.

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

Se você quiser um comportamento herdado para a pesquisa em duas fases, mas quiser o comportamento /permissive-, adicione a opção /Zc:twoPhase-.

Problemas de cabeçalho do Windows

A opção /permissive- é muito rigorosa para versões dos Kits do Windows anteriores do SDK do Windows Fall Creators Update (10.0.16299.0) ou do WDK (Windows Driver Kit) versão 1709. Recomendamos que você atualize para as versões mais recentes dos Kits do Windows para usar /permissive- no código do driver do dispositivo ou do Windows.

Determinados arquivos de cabeçalho no SDK da Atualização do Windows de abril de 2018 (10.0.17134.0), no SDK do Windows Fall Creators Update (10.0.16299.0) e no WDK (Windows Driver Kit) 1709 ainda têm problemas que os tornam incompatíveis com o uso de /permissive-. Para contornar esses problemas, recomendamos restringir o uso desses cabeçalhos apenas aos arquivos de código-fonte que os exigem e remover a opção /permissive- ao compilar esses arquivos de código-fonte específicos.

Esses cabeçalhos WRL do WinRT lançados no SDK de Atualização do Windows de abril de 2018 (10.0.17134.0) não são limpos com /permissive-. Para contornar esses problemas, não use /permissive-, ou use /permissive- com /Zc:twoPhase- quando trabalhar com estes cabeçalhos:

  • Problemas em 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
    
  • Problema em 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'
    

Esses cabeçalhos de Modo do Usuário lançados no SDK de Atualização do Windows de abril de 2018 (10.0.17134.0) não são limpos com /permissive-. Para contornar esses problemas, não use /permissive- ao trabalhar com estes cabeçalhos:

  • Problemas em 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
    
  • Problema em 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
    
  • Problemas em 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'
    

Estes problemas são específicos aos cabeçalhos do Modo de Usuário no SDK do Windows Fall Creators Update (10.0.16299.0):

  • Problema em um/Query.h

    Quando você usa a opção do compilador /permissive-, a estrutura tagRESTRICTION não é compilada devido ao membro 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;
    };
    

    Para resolver o problema, compile os arquivos que incluem Query.h sem a opção /permissive-.

  • Problema em um/cellularapi_oem.h

    Quando você usa a opção do compilador /permissive-, a declaração direta de enum UICCDATASTOREACCESSMODE gera um aviso:

    typedef enum UICCDATASTOREACCESSMODE UICCDATASTOREACCESSMODE; // C4471
    

    A declaração direta de um enum sem escopo é uma extensão da Microsoft. Para resolver esse problema, compile arquivos que incluem cellularapi_oem.h sem a opção /permissive- ou use a opção /wd para silenciar o aviso C4471.

  • Problema em um/omscript.h

    No C++03, uma conversão de um literal de cadeia de caracteres em BSTR (que é um typedef para wchar_t *) foi preterida, mas é permitida. No C++11, a conversão não é mais permitida.

    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 resolver esse problema, compile arquivos que incluem omscript.h sem a opção /permissive- ou use /Zc:strictStrings-.

Para definir esta opção do compilador no ambiente de desenvolvimento do Visual Studio

No Visual Studio 2017 versão 15.5 e versões posteriores, use este procedimento:

  1. Abra a caixa de diálogo Páginas de Propriedades do seu projeto.

  2. Selecione a página de propriedades Propriedades da Configuração>C/C++>Linguagem.

  3. Altere o valor da propriedade Modo de conformidade para Sim (/permissive-). Escolha OK ou Aplicar para salvar as alterações.

Em versões anteriores ao Visual Studio 2017 versão 15.5, use este procedimento:

  1. Abra a caixa de diálogo Páginas de Propriedades do seu projeto.

  2. Selecione a página de propriedades Propriedades de Configuração>C/C++>Linha de Comando.

  3. Insira a opção do compilador /permissive- na caixa Opções Adicionais. Escolha OK ou Aplicar para salvar as alterações.

Para definir essa opção do compilador via programação

Confira também

Opções do compilador MSVC
Sintaxe da linha de comando do compilador MSVC