Partilhar via


Melhorias de conformidade C++, alterações de comportamento e correções de bugs no Visual Studio 2022

Microsoft C/C++ no Visual Studio (MSVC) faz melhorias de conformidade e correções de bugs em cada versão. Este artigo lista as melhorias significativas por versão principal e, em seguida, por versão. Para ir diretamente para as alterações de uma versão específica, use os links no topo deste artigo.

Este documento lista as alterações no Visual Studio 2022.

Para alterações em versões anteriores do Visual Studio:

Versão Link de melhorias de conformidade
2019 melhorias de conformidade com C++ no Visual Studio 2019
2017 melhorias de conformidade com C++ no Visual Studio 2017
2003-2015 Visual C++ O que há de novo de 2003 a 2015

Melhorias de conformidade no Visual Studio 2022 versão 17.14

O Visual Studio 2022 versão 17.14 inclui as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Melhorias de conformidade

  • O fortalecimento da biblioteca padrão (P3471R4) transforma certas instâncias de comportamento indefinido na biblioteca padrão em uma chamada para __fastfail. Desativado por padrão. Defina _MSVC_STL_HARDENING=1 em todo o projeto para ativar.

Comportamento melhorado

  • Implementei "lápides destruidoras" para mitigar erros de uso após o livre. Desativado por padrão. Defina _MSVC_STL_DESTRUCTOR_TOMBSTONES=1 em todo o projeto para ativar.

Correções de erros

  • Corrigidos erros errantes do compilador ao usar <format> em um projeto CUDA.

  • Corrigido um problema do compilador em que o endereço de uma variável local podia "escapar" durante a avaliação de constexpr. Por exemplo:

    const unsigned & func() 
    {
      const int x = 0;
      constexpr const unsigned & r1 = x; // Previously accepted, now an error
      return r1;
    }
    
    auto r = func();  // Previously, the local address leaked
    

    Exemplo #2

    #include <initializer_list>
    
    void test()
    {
        constexpr std::initializer_list<int> xs { 1, 2, 3 };        // Previously accepted, now an error
    
        static constexpr std::initializer_list<int> ys { 1, 2, 3 }; // Correct usage - note use of static
    }
    
  • Código compilado com /permissive- não aceita mais uma combinação de friend e static em uma declaração. A correção geralmente é remover static da declaração. Por exemplo:

    struct S
    {
        friend static void f(); // Previously accepted, now emits error C2440: 'static' cannot be used with 'friend'
    };
    
  • Vinculação de referência a tipos qualificados voláteis fixados quando se referem a uma classe base ou derivada. Por exemplo:

    struct A {};
    struct B : public A {};
    
    void f(A&);                 // 1
    void f(const volatile A&);  // 2
    
    f(B{}); // Previously called 2. This is ill-formed under /permissive- or /Zc:referenceBinding. Chooses 1 if relaxed reference binding rules are enabled.
    

Para obter um resumo detalhado das alterações feitas na Biblioteca de Modelos Padrão, incluindo alterações de conformidade, correções de bugs e melhorias de desempenho, consulte STL Changelog VS 2022 17.14.

Melhorias de conformidade no Visual Studio 2022 versão 17.13

O Visual Studio 2022 versão 17.13 inclui as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Para obter um resumo detalhado das alterações feitas na Biblioteca de Modelos Padrão, incluindo alterações de conformidade, correções de bugs e melhorias de desempenho, consulte STL Changelog VS 2022 17.13.

Busca dependente de argumentos (ADL)

Construções de linguagem, como range-for e ligações estruturadas, têm regras especiais de pesquisa dependentes de argumentos para determinados identificadores, como begin, endou get. Anteriormente, essa pesquisa incluía candidatos do std namespace, mesmo quando o namespace std não faz parte do conjunto comum de namespaces associados para pesquisa dependente de argumento.

Os programas que introduziram declarações ao std para essas construções deixam de compilar. Em vez disso, as declarações devem estar em um namespace associado normal para os tipos envolvidos (possivelmente incluindo o namespace global).

template <typename T>
struct Foo {};

namespace std
{
    // To correct the program, move these declarations from std to the global namespace
    template <typename T>
    T* begin(Foo<T>& f);
    template <typename T>
    T* end(Foo<T>& f);
}

void f(Foo<int> foo)
{
   for (auto x : foo) // Previously compiled. Now emits error C3312: no callable 'begin' function found for type 'Foo<int>'
   {
      ...
   }
}

Não é possível modificar macros reservadas à implementação

Anteriormente, o compilador permitia alterar ou desdefinir certas macros fornecidas pela implementação, como _MSC_EXTENSIONS. Alterar a definição de certas macros pode resultar em comportamento indefinido.

A tentativa de alterar ou desdefinir determinados nomes de macro reservados agora resulta no aviso de nível 1 C5308. No modo /permissive-, esse aviso é tratado como um erro.

#undef _MSC_EXTENSIONS // Warning C5308: Modifying reserved macro name `_MSC_EXTENSIONS` may cause undefined behavior

Melhorias de conformidade no Visual Studio 2022 versão 17.12

O Visual Studio 2022 versão 17.12 inclui as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Para obter um resumo detalhado das alterações feitas na Biblioteca de Modelos Padrão, incluindo alterações de conformidade, correções de bugs e melhorias de desempenho, consulte STL Changelog VS 2022 17.12.

_com_ptr_t::operator bool() agora é explícito

Esta é uma alteração que quebra compatibilidade de origem/binária.

A conversão implícita para bool de instâncias de _com_ptr_t pode ser surpreendente ou levar a erros de compilação. C.164: Evitar operadores de conversão implícitos nas Diretrizes Principais do C++ desencorajam funções de conversão implícitas. E _com_ptr_t continha conversões implícitas para ambos bool e Interface*. Estas duas conversões implícitas podem levar a ambiguidades.

Para resolver isso, a conversão para bool agora é explícita. A conversão para Interface* permanece inalterada.

Uma macro é fornecida para desativar esse novo comportamento e restaurar a conversão implícita anterior. Compile com /D_COM_DISABLE_EXPLICIT_OPERATOR_BOOL para optar por não receber esta alteração. Recomendamos que você modifique o código para não depender de conversões implícitas.

Por exemplo:

#include <comip.h>

template<class Iface>
using _com_ptr = _com_ptr_t<_com_IIID<Iface, &__uuidof(Iface)>>;

int main()
{
   _com_ptr<IUnknown> unk;
   if (unk) // Still valid
   { 
      // ...
   }
   bool b = unk; // Still valid.
   int v = unk; // Previously permitted, now emits C2240: cannot convert from '_com_ptr_t<_com_IIID<IUnknown,& _GUID_00000000_0000_0000_c000_000000000046>>' to 'int'
}

As expressões constantes já não são sempre noexcept no modo permissivo

Esta é uma alteração que quebra compatibilidade de origem/binária.

Uma expressão constante era sempre noexcept, mesmo que envolvesse uma chamada a uma função declarada com uma especificação de exceção que pode lançar. Este texto foi removido no C++17, embora o compilador do Microsoft Visual C++ ainda o suportasse no modo /permissive em todas as versões de linguagem C++.

Esse comportamento de modo /permissive é removido. As expressões constantes não recebem mais um comportamento implícito especial.

O especificador noexcept em funções constexpr é agora respeitado em todos os modos. Essa alteração é necessária para a implementação correta de resoluções de problemas principais posteriores que dependem do comportamento padrão noexcept.

Por exemplo:

constexpr int f(bool b) noexcept(false)
{ 
    if (b)
    {
        throw 1;
    }
    else
    {
        return 1;
    }
}

void g(bool b)
{
   noexcept(f(b)); // false. No change to behavior
   noexcept(f(true)); // false. No change to behavior
   noexcept(f(false)); // false. Was true in /permissive mode only in previous versions.
}

Melhorias de conformidade no Visual Studio 2022 versão 17.11

O Visual Studio 2022 versão 17.11 inclui as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Para obter um resumo detalhado das alterações feitas na Biblioteca de Modelos Padrão, incluindo alterações de conformidade, correções de bugs e melhorias de desempenho, consulte STL Changelog VS 2022 17.11.

Por P3142R0, agora é fácil gerar uma linha em branco com println. Este recurso está disponível ao compilar com /std:c++latest. Antes dessa mudança, você escreveu: println(""); Agora você escreve: println();.

  • println(); equivale a println(stdout);
  • println(FILE* stream); equivale a println(stream, "\n");

Implementado range_formatter

De acordo com P2286R8, range_formatter está agora implementado. Este recurso está disponível ao compilar com /std:c++latest.

Melhorias de conformidade no Visual Studio 2022 versão 17.10

O Visual Studio 2022 versão 17.10 inclui as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Para obter um resumo detalhado das alterações feitas na Biblioteca de Modelos Padrão, incluindo alterações de conformidade, correções de bugs e melhorias de desempenho, consulte STL Changelog VS 2022 17.10.

Especialização do operador de conversão com tipo de retorno explicitamente especificado

O compilador costumava especializar incorretamente os operadores de conversão em alguns casos, o que podia levar a um tipo de retorno incompatível. Essas especializações inválidas não acontecem mais. Esta é uma alteração que quebra o código-fonte.

// Example 1
struct S
{
    template<typename T> operator const T*();
};

void test()
{
    S{}.operator int*(); // this is invalid now
    S{}.operator const int*(); // this is valid
}
// Example 2
// In some cases, the overload resolution result may change
struct S
{
    template <typename T> operator T*(); // overload 1
    template <typename T> operator const T*(); // overload 2
};

void test()
{
    S{}.operator int*(); // this used to call overload 2, now it calls overload 1
}

Adicionado suporte para #elifdef e #elifndef

Suporte adicionado para WG21 P2334R1 (C++23) e WG14 N2645 (C++23), que introduziu as diretivas de pré-processador #elifdef e #elifndef. Requer /std:clatest ou /std:c++latest.

Antes:

#ifdef __cplusplus
  #include <atomic>
#elif !defined(__STDC_NO_ATOMICS__)
  #include <stdatomic.h>
#else
  #include <custom_atomics_library.h>
#endif

Depois:

#ifdef __cplusplus
  #include <atomic>
#elifndef __STDC_NO_ATOMICS__
  #include <stdatomic.h>
#else
  #include <custom_atomics_library.h>
#endif

Aplicação de _Alignas num tipo estruturado em C

Aplica-se à linguagem C (C17 e posteriores). Também adicionado ao Microsoft Visual Studio 17.9

Em versões do Visual C++ anteriores ao Visual Studio 2022 versão 17.9, se o especificador de _Alignas aparecesse ao lado de um tipo estruturado em uma declaração, ele não era aplicado corretamente de acordo com o ISO-C Standard.

// compile with /std:c17
#include <stddef.h>

struct Outer
{
    _Alignas(32) struct Inner { int i; } member1;
    struct Inner member2;
};
static_assert(offsetof(struct Outer, member2)==4, "incorrect alignment");

De acordo com o ISO-C Standard, este código deve ser compilado sem static_assert emitir um diagnóstico.

A diretiva _Alignas aplica-se apenas à variável membro member1. Não deve alterar o alinhamento de struct Inner. No entanto, antes do Visual Studio 17.9.1, o diagnóstico "alinhamento incorreto" era emitido. O compilador alinhou member2 a um deslocamento de 32 bytes dentro do tipo struct Outer.

Esta é uma alteração de quebra binária, portanto, um aviso é emitido quando essa alteração entra em vigor. O aviso C5274 é agora emitido no nível de aviso 1 para o exemplo anterior: warning C5274: behavior change: _Alignas no longer applies to the type 'Inner' (only applies to declared data objects).

Além disso, em versões anteriores do Visual Studio, quando o especificador de _Alignas aparecia ao lado de uma declaração de tipo anônimo, ele era ignorado.

// compile with /std:c17
#include <stddef.h>
struct S
{
    _Alignas(32) struct { int anon_member; };
    int k;
};

static_assert(offsetof(struct S, k)==4, "incorrect offsetof");
static_assert(sizeof(struct S)==32, "incorrect size");

Anteriormente, ambas as instruções static_assert falharam ao compilar esse código. Agora o código compila, mas emite os seguintes avisos de nível 1:

warning C5274: behavior change: _Alignas no longer applies to the type '<unnamed-tag>' (only applies to declared data objects)
warning C5273: behavior change: _Alignas on anonymous type no longer ignored (promoted members will align)

Para obter o comportamento anterior, substitua _Alignas(N) por __declspec(align(N)). Ao contrário _Alignas, declspec(align) se aplica ao tipo.

Aviso melhorado C4706

Esta é uma alteração que quebra o código-fonte. Anteriormente, o compilador não detetava a convenção de encapsular uma atribuição entre parênteses, se a atribuição fosse pretendida, para suprimir aviso C4706 sobre atribuição dentro de uma expressão condicional. O compilador agora deteta os parênteses e suprime o aviso.

#pragma warning(error: 4706)

struct S
{
   auto mf()
   {
      if (value = 9)
         return value + 4;
      else
         return value;
   }

   int value = 9;
};

O compilador agora também emite o aviso nos casos em que a função não é referenciada. Anteriormente, como mf é uma função embutida que não é referenciada, o aviso C4706 não foi emitido para esse código. Agora o aviso é emitido:

error C4706: assignment used as a condition
note: if an assignment is intended you can enclose it in parentheses, '(e1 = e2)', to silence this warning

Para corrigir esse aviso, use um operador de igualdade, value == 9, se for isso que se pretendia. Ou, coloque a atribuição entre parênteses, (value = 9), se a atribuição for intencional. Caso contrário, uma vez que a função não é referenciada, remova-a.

Melhorias de conformidade no Visual Studio 2022 versão 17.9

O Visual Studio 2022 versão 17.9 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Para obter um resumo mais amplo das alterações feitas na Biblioteca de Modelos Padrão, consulte STL Changelog VS 2022 17.9.

Aplicação de _Alignas num tipo estruturado em C

Em versões do Visual C++ anteriores ao Visual Studio 2022 versão 17.9, quando _Alignas aparecia ao lado de um tipo de estrutura em uma declaração, ele não era aplicado corretamente de acordo com o ISO-C Standard. Por exemplo:

// compile with /std:c17
#include <stddef.h>
struct Outer
{
    _Alignas(32) struct Inner { int i; } member1;
    struct Inner member2;
};
static_assert(offsetof(struct Outer, member2)==4, "incorrect alignment");

De acordo com o ISO-C Standard, este código deve ser compilado sem que o static_assert emita um diagnóstico. A diretiva _Alignas aplica-se apenas à variável membro member1. Não deve alterar o alinhamento de struct Inner. No entanto, antes da versão 17.9.1 do Visual Studio, o diagnóstico "alinhamento incorreto" foi emitido. O compilador alinhou member2 a um deslocamento de 32 bytes dentro de struct Outer.

Corrigir isso é uma alteração de quebra binária, então quando essa mudança de comportamento é aplicada um aviso é emitido. Para o código anterior, o Aviso C5274, "_Alignas já não se aplica ao tipo 'Inner' (aplica-se apenas a objetos de dados declarados)", é agora emitido ao nível de aviso 1.

Em versões anteriores do Visual Studio, _Alignas era ignorado quando aparecia ao lado de uma declaração de tipo anônimo. Por exemplo:

// compile with /std:c17
#include <stddef.h>
struct S {
    _Alignas(32) struct { int anon_member; };
    int k;
};
static_assert(offsetof(struct S, k)==4, "incorrect offsetof");
static_assert(sizeof(struct S)==32, "incorrect size");

Anteriormente, ambas as instruções static_assert falharam ao compilar esse código. O código agora é compilado, mas com os seguintes avisos de nível 1:

warning C5274: behavior change: _Alignas no longer applies to the type '<unnamed-tag>' (only applies to declared data objects)
warning C5273: behavior change: _Alignas on anonymous type no longer ignored (promoted members will align)

Se você quiser o comportamento anterior, substitua _Alignas(N) por __declspec(align(N)). Ao contrário _Alignas, declspec(align) pode ser aplicado a um tipo.

__VA_OPT__ está habilitado como uma extensão em /Zc:preprocessor

__VA_OPT__ foi adicionado ao C++20 e C23. Antes de sua adição, não havia uma maneira padrão de elidir uma vírgula em uma macro variádica. Para proporcionar melhor compatibilidade retroativa, __VA_OPT__ é ativado pelo pré-processador baseado em token /Zc:preprocessor em todas as versões de idioma.

Por exemplo, isso agora compila sem erro:

#define LOG_WRAPPER(message, ...) WRITE_LOG(__LINE__, message __VA_OPT__(, __VA_ARGS__))

// Failed to build under /std:c11, now succeeds.
LOG_WRAPPER("Log message");
LOG_WRAPPER("Log message with %s", "argument")

Língua C23

Para C23, o seguinte está disponível ao usar a opção de compilador /std:clatest:

typeof
typeof_unqual

Estão disponíveis para todas as versões da linguagem C:

__typeof__
__typeof_unqual__

Biblioteca padrão C++

recursos do C++23

Melhorias de conformidade no Visual Studio 2022 versão 17.8

O Visual Studio 2022 versão 17.8 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

/FU emite um erro

O compilador C costumava aceitar a opção /FU, mesmo que não suporte a compilação gerenciada há algum tempo. Agora emite um erro. Os projetos que passam essa opção precisam restringi-la apenas a projetos C++/CLI.

Biblioteca padrão C++

Os módulos nomeados C++23 std e std.compat agora estão disponíveis ao compilar com /std:c++20.

Para obter um resumo mais amplo das alterações feitas na Biblioteca Padrão C++, consulte STL Changelog VS 2022 17.8.

Melhorias de conformidade no Visual Studio 2022 versão 17.7

O Visual Studio 2022 versão 17.7 contém as seguintes melhorias de conformidade destacadas, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Adicionado /std:clatest ao compilador C

Essa opção se comporta como a opção /std:c++latest para o compilador C++. O switch permite todos os recursos de compilador e biblioteca padrão atualmente implementados propostos para o próximo projeto de padrão C, bem como alguns recursos experimentais e em andamento.

Biblioteca padrão C++

A biblioteca <print> agora é suportada. Consulte P2093R14 Saída formatada.

Implementado views::cartesian_product.

Para obter um resumo mais amplo das alterações feitas na Biblioteca de Modelos Padrão, consulte STL Changelog VS 2022 17.7.

using conformidade

Anteriormente, a diretiva using podia fazer com que nomes de namespaces usados permanecessem visíveis quando não deveriam. Isso pode fazer com que a pesquisa de nome não qualificada encontre um nome em um namespace, mesmo quando não há nenhuma diretiva using ativa.

Aqui estão alguns exemplos do novo e do velho comportamento.
As referências nos comentários a seguir a "(1)" significam a chamada para f<K>(t) no namespace A:

namespace A
{ 
    template<typename K, typename T> 
    auto f2(T t)
    { 
        return f<K>(t); // (1) Unqualified lookup should not find anything
    } 
} 

namespace B
{ 
    template<typename K, typename T> 
    auto f(T t) noexcept
    { // Previous behavior: This function was erroneously found during unqualified lookup at (1)
        return A::f2<K>(t); 
    } 
} 

namespace C
{ 
    template<typename T> 
    struct S {}; 

    template<typename, typename U> 
    U&& f(U&&) noexcept; // New behavior: ADL at (1) correctly finds this function 
} 

namespace D
{ 
    using namespace B; 

    void h()
    { 
        D::f<void>(C::S<int>()); 
    } 
} 

O mesmo problema subjacente pode fazer com que o código compilado anteriormente seja rejeitado:

#include <memory>
namespace Addin {}
namespace Gui
{
    using namespace Addin;
}

namespace Addin
{
    using namespace std;
}

// This previously compiled, but now emits error C2065 for undeclared name 'allocator'.
// This should be declared as 'std::allocator<T*>' because the using directive nominating
// 'std' is not active at this point.
template <class T, class U = allocator<T*>>
class resource_list
{
};

namespace Gui
{
    typedef resource_list<int> intlist;
}

Melhorias de conformidade no Visual Studio 2022 versão 17.6

O Visual Studio 2022 versão 17.6 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

As atribuições de volatile composto deixaram de ser obsoletas

C++20 tornou obsoleta a aplicação de certos operadores a tipos qualificados com volatile. Por exemplo, quando o código a seguir é compilado com cl /std:c++20 /Wall test.cpp:

void f(volatile int& expr)
{
   ++expr;
}

O compilador produz test.cpp(3): warning C5214: applying '++' to an operand with a volatile qualified type is deprecated in C++20.

Em C++20, os operadores de atribuição compostos (operadores do formulário @=) foram preteridos. No C++23, os operadores compostos excluídos no C++20 não são mais preteridos. Por exemplo, em C++23 o código a seguir não produz um aviso, enquanto em C++20:

void f(volatile int& e1, int e2)
{
   e1 += e2;
}

Para obter mais informações sobre essa alteração, consulte CWG:2654

Reescrever a igualdade nas expressões é uma alteração menos disruptiva (P2468R2)

Em C++20, P2468R2 alterou o compilador para aceitar código como:

struct S
{
    bool operator==(const S&);
    bool operator!=(const S&);
};
bool b = S{} != S{};

O compilador aceita esse código, o que significa que o compilador é mais rigoroso com códigos como:

struct S
{
  operator bool() const;
  bool operator==(const S&);
};

bool b = S{} == S{};

A versão 17.5 do compilador aceita este programa. A versão 17.6 do compilador rejeita-o. Para corrigi-lo, adicione const ao operator== para remover a ambiguidade. Ou, adicione um operator!= correspondente à definição, conforme mostrado no exemplo a seguir:

struct S
{
  operator bool() const;
  bool operator==(const S&);
  bool operator!=(const S&);
};

bool b = S{} == S{};

Microsoft C/C++ compilador nas versões 17.5 e 17.6 aceitam o programa anterior e chamam S::operator== em ambas as versões.

O modelo de programação geral descrito em P2468R2 é que, se houver um operator!= correspondente para um tipo, ele normalmente suprime o comportamento de reescrita. Adicionar um operator!= correspondente é a correção sugerida para o código compilado anteriormente em C++17. Para obter mais informações, consulte Programming Model.

Melhorias de conformidade no Visual Studio 2022 versão 17.4

O Visual Studio 2022 versão 17.4 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Tipos subjacentes de enum não delimitados, sem tipo fixo

Em versões do Visual Studio anteriores ao Visual Studio 2022 versão 17.4, o compilador C++ não determinou corretamente o tipo subjacente de uma enumeração sem escopo sem nenhum tipo base fixo. Em /Zc:enumTypes, agora implementamos corretamente o comportamento padrão.

O padrão C++ requer que o tipo subjacente de um enum seja grande o suficiente para manter todos os enumeradores nesse enum. Enumeradores suficientemente grandes podem definir o tipo subjacente do enum como unsigned int, long longou unsigned long long. Anteriormente, esses tipos de enum sempre tinham um tipo subjacente de int no compilador da Microsoft, independentemente dos valores do enumerador.

Quando ativada, a opção /Zc:enumTypes é uma fonte de alterações possíveis e de quebras binárias. Ele está desativado por padrão e não habilitado pelo /permissive-, porque a correção pode afetar a compatibilidade binária. Alguns tipos de enumeração mudam de tamanho quando a correção conforme é habilitada. Certos cabeçalhos do SDK do Windows incluem essas definições de enumeração.

Exemplo

enum Unsigned
{
    A = 0xFFFFFFFF // Value 'A' does not fit in 'int'.
};

// Previously, failed this static_assert. Now passes with /Zc:enumTypes.
static_assert(std::is_same_v<std::underlying_type_t<Unsigned>, unsigned int>);

template <typename T>
void f(T x)
{
}

int main()
{
    // Previously called f<int>, now calls f<unsigned int>.
    f(+A);
}

// Previously this enum would have an underlying type of `int`, but Standard C++ requires this to have
// a 64-bit underlying type. Using /Zc:enumTypes changes the size of this enum from 4 to 8, which could
// impact binary compatibility with code compiled with an earlier compiler version or without the switch.
enum Changed
{
    X = -1,
    Y = 0xFFFFFFFF
};

Tipos de enumeradores dentro de uma definição de enum sem tipo subjacente fixo

Em versões do Visual Studio anteriores ao Visual Studio 2022 versão 17.4, o compilador C++ não modelou corretamente os tipos de enumeradores. Pode assumir um tipo incorreto em enumerações sem um tipo subjacente fixo antes do fecho da enumeração. Em /Zc:enumTypes, o compilador agora implementa corretamente o comportamento padrão.

O padrão C++ especifica que, dentro de uma definição de enumeração de nenhum tipo subjacente fixo, os inicializadores determinam os tipos de enumeradores. Ou, para os enumeradores sem inicializador, pelo tipo do enumerador anterior (contabilizando sobrecarga). Anteriormente, esses enumeradores sempre recebiam o tipo deduzido da enumeração, com um espaço reservado para o tipo subjacente (normalmente int).

Quando ativada, a opção /Zc:enumTypes é uma fonte de alterações possíveis e de quebras binárias. Ele está desativado por padrão e não habilitado pelo /permissive-, porque a correção pode afetar a compatibilidade binária. Alguns tipos de enumeração mudam de tamanho quando a correção conforme é habilitada. Certos cabeçalhos do SDK do Windows incluem essas definições de enumeração.

Exemplo

enum Enum {
    A = 'A',
    B = sizeof(A)
};

static_assert(B == 1); // previously failed, now succeeds under /Zc:enumTypes

Neste exemplo, o enumerador A deve ter o tipo char antes da chave de fechamento da enumeração, portanto B deve ser inicializado usando sizeof(char). Antes da correção /Zc:enumTypes, A tinha o tipo de enumeração Enum com um tipo subjacente deduzido int, e B foi inicializado usando sizeof(Enum), ou 4.

Melhorias de conformidade no Visual Studio 2022 versão 17.3

O Visual Studio 2022 versão 17.3 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

C: Verificação de compatibilidade do modificador melhorada entre ponteiros

O compilador C não comparou corretamente os modificadores entre ponteiros, especialmente void*. Este defeito pode resultar num diagnóstico inadequado de incompatibilidade entre const int** e void* e compatibilidade entre int* volatile* e void*.

Exemplo

void fn(void* pv) { (pv); }

int main()
{
    int t = 42;
    int* pt = &t;
    int* volatile * i = &pt;
    fn(i);    // Now raises C4090
    const int** j = &pt;
    fn(j);    // No longer raises C4090
}

Melhorias de conformidade no Visual Studio 2022 versão 17.2

O Visual Studio 2022 versão 17.2 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Avisos de caracteres bidirecionais não finalizados

O Visual Studio 2022 versão 17.2 adiciona aviso de nível 3 C5255 para caracteres bidirecionais Unicode não terminados em comentários e cadeias de caracteres. O aviso aborda uma preocupação de segurança descrita em Trojan Source: Invisible Vulnerabilities por Nicholas Boucher e Ross Anderson. Para obter mais informações sobre caracteres bidirecionais Unicode, consulte Unicode® Standard Annex #9: UNICODE BIDIRECTIONAL ALGORITHM.

Aviso C5255 só aborda arquivos que, após a conversão, contêm caracteres bidirecionais Unicode. Esse aviso se aplica a arquivos UTF-8, UTF-16 e UTF-32, portanto, a codificação de origem adequada deve ser fornecida. Esta alteração é uma alteração de quebra de fonte.

Exemplo (antes/depois)

Nas versões do Visual Studio anteriores ao Visual Studio 2022 versão 17.2, um caractere bidirecional não terminado não produzia um aviso. O Visual Studio 2022 versão 17.2 produz o aviso C5255:

// bidi.cpp
int main() {
    const char *access_level = "user";
    // The following source line contains bidirectional Unicode characters equivalent to:
    //    if ( strcmp(access_level, "user\u202e \u2066// Check if admin \u2069 \u2066") ) {
    // In most editors, it's rendered as:
    //    if ( strcmp(access_level, "user") ) { // Check if admin
    if ( strcmp(access_level, "user‮ ⁦// Check if admin ⁩ ⁦") ) {
        printf("You are an admin.\n");
    }
    return 0;
}

/* build output
bidi.cpp(8): warning C5255: unterminated bidirectional character encountered: 'U+202e'
bidi.cpp(8): warning C5255: unterminated bidirectional character encountered: 'U+2066'
*/

from_chars() float desempate

O Visual Studio 2022 versão 17.2 corrige um bug nas regras de desempate <charconv>from_chars()float que produzia resultados incorretos. Esse bug afetou cadeias decimais que estavam no ponto médio exato de valores de float consecutivos, dentro de um intervalo estreito. (Os menores e maiores valores afetados foram 32768.009765625 e 131071.98828125, respectivamente.) A regra de desempate queria arredondar para "par", e "par" por acaso significava "para baixo", mas a implementação arredondou incorretamente para "cima" (double não foi afetado). Para obter mais informações e detalhes de implementação, consulte microsoft/STL#2366.

Essa alteração afeta o comportamento do tempo de execução no intervalo especificado de casos:

Exemplo

// from_chars_float.cpp
#include <cassert>
#include <charconv>
#include <cstdio>
#include <string_view>
#include <system_error>
using namespace std;
int main() {
    const double dbl  = 32768.009765625;
    const auto sv     = "32768.009765625"sv;
    float flt         = 0.0f;
    const auto result = from_chars(sv.data(), sv.data() + sv.size(), flt);
    assert(result.ec == errc{});
    printf("from_chars() returned: %.1000g\n", flt);
    printf("This rounded %s.\n", flt < dbl ? "DOWN" : "UP");
}

Em versões anteriores ao Visual Studio 2022 versão 17.2:

C:\Temp>cl /EHsc /nologo /W4 /std:c++17 from_chars_float.cpp && from_chars_float
from_chars_float.cpp
from_chars() returned: 32768.01171875
This rounded UP.

No Visual Studio 2022 versão 17.2 e posteriores:

C:\Temp>cl /EHsc /nologo /W4 /std:c++17 from_chars_float.cpp && from_chars_float
from_chars_float.cpp
from_chars() returned: 32768.0078125
This rounded DOWN.

/Zc:__STDC__ disponibiliza __STDC__ para C

O padrão C requer que uma implementação C em conformidade defina __STDC__ como 1. Devido ao comportamento do UCRT, que não expõe as funções POSIX quando __STDC__ é 1, não é possível definir essa macro para C por padrão sem introduzir alterações significativas nas versões de idioma estável. O Visual Studio 2022 versão 17.2 e posterior adicionam uma opção de conformidade /Zc:__STDC__ que define essa macro. Não há nenhuma versão negativa da opção. Atualmente, planejamos usar essa opção por padrão para versões futuras do C.

Esta alteração é uma alteração de quebra de fonte. Aplica-se quando o modo C11 ou C17 está ativado (/std:c11 ou /std:c17) e /Zc:__STDC__ é especificado.

Exemplo

// test__STDC__.c
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
#if __STDC__
    int f = _open("file.txt", _O_RDONLY);
    _close(f);
#else
    int f = open("file.txt", O_RDONLY);
    close(f);
#endif
}

/* Command line behavior

C:\Temp>cl /EHsc /W4 /Zc:__STDC__ test__STDC__.c && test__STDC__

*/

Aviso de falta de chavetas

Aviso C5246 indica chavetas ausentes durante a inicialização agregada de um subobjeto. Antes do Visual Studio 2022 versão 17.2, o aviso não tratava do caso de um struct anônimo ou union.

Esta alteração é uma alteração de quebra de fonte. Aplica-se quando o aviso C5246, desativado por padrão, está ativado.

Exemplo

No Visual Studio 2022 versão 17.2 e posterior, esse código agora causa um erro:

struct S {
   union {
      float f[4];
      double d[2];
   };
};

void f()
{
   S s = { 1.0f, 2.0f, 3.14f, 4.0f };
}

/* Command line behavior
cl /Wall /c t.cpp

t.cpp(10): warning C5246: 'anonymous struct or union': the initialization of a subobject should be wrapped in braces
*/

Para resolver este problema, acrescente chaves ao inicializador.

void f()
{
   S s = { { 1.0f, 2.0f, 3.14f, 4.0f } };
}

Melhorias de conformidade no Visual Studio 2022 versão 17.1

O Visual Studio 2022 versão 17.1 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Detetar padrão de captura mal formado em expressões lambda não locais

O C++ Standard só permite que uma expressão lambda no âmbito de um bloco tenha um capture-default. No Visual Studio 2022 versão 17.1 e posterior, o compilador deteta quando um padrão de captura não é permitido em uma expressão lambda não local. Emite um novo aviso de nível 4, C5253.

Esta alteração é uma alteração de quebra de fonte. Ele se aplica em qualquer modo que use o novo processador lambda: /Zc:lambda, /std:c++20ou /std:c++latest.

Exemplo

No Visual Studio 2022 versão 17.1, esse código agora emite um erro:

#pragma warning(error:5253)

auto incr = [=](int value) { return value + 1; };

// capture_default.cpp(3,14): error C5253: a nonlocal lambda cannot have a capture default
// auto incr = [=](int value) { return value + 1; };
//              ^

Para corrigir esse problema, remova o padrão de captura:

#pragma warning(error:5253)

auto incr = [](int value) { return value + 1; };

C4028 agora é C4133 para operações de conversão de função para ponteiro

Antes do Visual Studio 2022 versão 17.1, o compilador reportava uma mensagem de erro incorreta em certas comparações de ponteiros para funções no código C. A mensagem incorreta foi apresentada quando se compararam dois ponteiros de função que tinham o mesmo número de argumentos, mas tipos incompatíveis. Agora, emitimos um aviso diferente que indica incompatibilidade de ponteiro para função, em vez de incompatibilidade de parâmetros de função.

Esta alteração é uma alteração de quebra de fonte. Aplica-se quando o código é compilado como C.

Exemplo

int f1(int); 
int f2(char*); 
int main(void) 
{ 
    return (f1 == f2); 
}
// Old warning:
// C4028: formal parameter 1 different from declaration
// New warning:
// C4113: 'int (__cdecl *)(char *)' differs in parameter lists from 'int (__cdecl *)(int)'

Erro em um static_assert não dependente

No Visual Studio 2022 versão 17.1 e posterior, se a expressão associada a um static_assert não for uma expressão dependente, o compilador avaliará a expressão quando ela for analisada. Se a expressão for avaliada como false, o compilador emitirá um erro. Anteriormente, se o static_assert estivesse dentro do corpo de um modelo de função (ou dentro do corpo de uma função membro de um modelo de classe), o compilador não executaria essa análise.

Esta alteração é uma alteração de quebra de fonte. Aplica-se em qualquer modalidade que implique /permissive- ou /Zc:static_assert. Essa alteração no comportamento pode ser desabilitada usando a opção /Zc:static_assert- compilador.

Exemplo

No Visual Studio 2022 versão 17.1 e posterior, esse código agora causa um erro:

template<typename T>
void f()
{
   static_assert(false, "BOOM!");
}

Para corrigir esse problema, torne a expressão dependente. Por exemplo:

template<typename>
constexpr bool dependent_false = false;

template<typename T>
void f()
{
   static_assert(dependent_false<T>, "BOOM!");
}

Com essa alteração, o compilador só emite um erro se o modelo de função f for instanciado.

Melhorias de conformidade no Visual Studio 2022 versão 17.0

O Visual Studio 2022 versão 17.0 contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C/C++.

Aviso sobre a largura do campo de bits para o tipo de enumeração

Quando você declara uma instância de um tipo de enumeração como um campo de bits, a largura do campo de bits deve acomodar todos os valores possíveis da enumeração. Caso contrário, o compilador emite uma mensagem de diagnóstico. Considere este exemplo: Considere:

enum class E : unsigned { Zero, One, Two };

struct S {
  E e : 1;
};

Um programador pode esperar que o membro da classe S::e possa manter qualquer um dos valores enum explicitamente nomeados. Dado o número de elementos de enumeração, não é possível. O campo de bits não pode cobrir o intervalo de valores explicitamente fornecidos de E (conceitualmente, o domínio de E). Para resolver a preocupação de que a largura do campo de bits não é grande o suficiente para o domínio da enumeração, um novo aviso (desativado por padrão) é adicionado ao MSVC:

t.cpp(4,5): warning C5249: 'S::e' of type 'E' has named enumerators with values that cannot be represented in the given bit field width of '1'.
  E e : 1;
    ^
t.cpp(1,38): note: see enumerator 'E::Two' with value '2'
enum class E : unsigned { Zero, One, Two };
                                     ^

Esse comportamento do compilador é uma alteração de quebra binária e de origem que afeta todos os modos /std e /permissive.

Erro na comparação ordenada do ponteiro com nullptr ou 0

O padrão C++ inadvertidamente permitiu uma comparação ordenada de ponteiro contra nullptr ou 0. Por exemplo:

bool f(int *p)
{
   return p >= 0;
}

O documento WG21 N3478 removeu essa supervisão. Esta alteração é implementada no MSVC. Quando o exemplo é compilado usando /permissive- (e /diagnostics:caret), ele emite o seguinte erro:

t.cpp(3,14): error C7664: '>=': ordered comparison of pointer and integer zero ('int *' and 'int')
    return p >= 0;
             ^

Este comportamento do compilador é uma alteração que quebra a compatibilidade de origem e binária, afetando o código compilado usando /permissive- em todos os modos /std.

Ver também

Conformidade da Linguagem Microsoft C/C++