Partilhar via


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

Microsoft C++ (MSVC) Build Tools no Visual Studio faz melhorias de conformidade e correções de bugs em cada versão. Este artigo lista as melhorias por versão principal e, em seguida, por versão. Para ir diretamente para as alterações de uma versão específica, use a lista abaixo Neste artigo.

Este documento lista as alterações no Visual Studio 2019. Para obter um guia para as alterações no Visual Studio 2022, consulte Aprimoramentos de conformidade C++ no Visual Studio 2022. Para alterações no Visual Studio 2017, consulte Aprimoramentos de conformidade C++ no Visual Studio 2017. Para obter uma lista completa dos aprimoramentos de conformidade anteriores, consulte Visual C++ What's New 2003 to 2015.

Melhorias de conformidade no Visual Studio 2019 RTW (versão 16.0)

O Visual Studio 2019 RTW contém as seguintes melhorias de conformidade, correções de bugs e alterações de comportamento no compilador Microsoft C++.

Observação

Os recursos do C++20 estavam disponíveis apenas no /std:c++latest modo no Visual Studio 2019 até que a implementação do C++20 fosse considerada concluída. O Visual Studio 2019 versão 16.11 introduz o modo de /std:c++20 compilador. Neste artigo, os recursos que originalmente exigiam /std:c++latest o modo agora funcionam no /std:c++20 modo ou posterior nas versões mais recentes do Visual Studio. Atualizamos a documentação para mencionar /std:c++20, mesmo que essa opção não estivesse disponível quando os recursos foram lançados pela primeira vez.

Suporte aprimorado a módulos para modelos e deteção de erros

Os módulos agora estão oficialmente no padrão C++20. Suporte aprimorado foi adicionado no Visual Studio 2017 versão 15.9. Para obter mais informações, consulte Melhor suporte a modelos e deteção de erros em módulos C++ com MSVC 2017 versão 15.9.

Especificação modificada do tipo de agregado

A especificação de um tipo de agregado foi alterada em C++20 (consulte Proibir agregações com construtores declarados pelo usuário). No Visual Studio 2019, em /std:c++latest (ou /std:c++20 no Visual Studio 2019 versão 16.11 e posterior), uma classe com qualquer construtor declarado pelo usuário (por exemplo, incluindo um construtor declarado = default ou = delete) não é uma agregação. Anteriormente, apenas construtores fornecidos pelo usuário desqualificavam uma classe de ser um agregado. Essa alteração coloca mais restrições sobre como esses tipos podem ser inicializados.

O código a seguir compila sem erros no Visual Studio 2017, mas gera erros C2280 e C2440 no Visual Studio 2019 em /std:c++20 ou /std:c++latest:

struct A
{
    A() = delete; // user-declared ctor
};

struct B
{
    B() = default; // user-declared ctor
    int i = 0;
};

A a{}; // ill-formed in C++20, previously well-formed
B b = { 1 }; // ill-formed in C++20, previously well-formed

Suporte parcial para operator <=>

P0515R3 O C++20 introduz o operador de <=> comparação de três vias, também conhecido como "operador de nave espacial". Visual Studio 2019 versão 16.0 no /std:c++latest modo introduz suporte parcial para o operador gerando erros para sintaxe que agora não é permitido. Por exemplo, o código a seguir compila sem erros no Visual Studio 2017, mas gera vários erros no Visual Studio 2019 em /std:c++20 ou /std:c++latest:

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

template <bool (S::*)(const S&) const>
struct U { };

int main(int argc, char** argv)
{
    U<&S::operator<=> u; // In Visual Studio 2019 raises C2039, 2065, 2146.
}

Para evitar os erros, insira um espaço na linha ofensiva antes do colchete angular final: U<&S::operator<= > u;.

Referências a tipos com qualificadores de CV incompatíveis

Observação

Essa alteração afeta apenas as versões 16.0 a 16.8 do Visual Studio 2019. Ele foi revertido a partir do Visual Studio 2019 versão 16.9

Anteriormente, o MSVC permitia a vinculação direta de uma referência de um tipo com qualificadores cv incompatíveis abaixo do nível superior. Esta ligação poderia permitir a modificação de dados supostamente const referidos na referência.

O compilador para as versões 16.0 a 16.8 do Visual Studio 2019 cria um temporário, como era exigido pelo padrão na época. Mais tarde, o padrão mudou retroativamente, corrigindo o comportamento anterior do Visual Studio 2017 e anteriores e o comportamento do Visual Studio 2019 versão 16.0 a 16.8 errado. Consequentemente, essa alteração foi revertida a partir do Visual Studio 2019 versão 16.9.

Consulte Tipos semelhantes e vinculação de referência para uma alteração relacionada.

Como exemplo, no Visual Studio 2017, o código a seguir é compilado sem avisos. No Visual Studio 2019 versões 16.0 a 16.8, o compilador gera aviso C4172. A partir do Visual Studio 2019 versão 16.9, o código mais uma vez compila sem avisos:

struct X
{
    const void* const& PData() const
    {
        return _pv;
    }

    void* _pv;
};

int main()
{
    X x;
    auto p = x.PData(); // C4172 <func:#1 "?PData@X@@QBEABQBXXZ"> returning address of local variable or temporary
}

reinterpret_cast de uma função sobrecarregada

O argumento para reinterpret_cast não é um dos contextos em que o endereço de uma função sobrecarregada é permitido. O código a seguir é compilado sem erros no Visual Studio 2017, mas no Visual Studio 2019 ele gera o erro C2440:

int f(int) { return 1; }
int f(float) { return .1f; }
using fp = int(*)(int);

int main()
{
    fp r = reinterpret_cast<fp>(&f); // C2440: cannot convert from 'overloaded-function' to 'fp'
}

Para evitar o erro, use uma conversão permitida para este cenário:

int f(int);
int f(float);
using fp = int(*)(int);

int main()
{
    fp r = static_cast<fp>(&f); // or just &f;
}

Fechos lambda

No C++14, os tipos de fechamento lambda não são literais. A principal consequência dessa regra é que um lambda não pode ser atribuído a uma constexpr variável. O código a seguir é compilado sem erros no Visual Studio 2017, mas no Visual Studio 2019 ele gera o erro C2127:

int main()
{
    constexpr auto l = [] {}; // C2127 'l': illegal initialization of 'constexpr' entity with a non-constant expression
}

Para evitar o erro, remova o constexpr qualificador ou altere o modo de conformidade para /std:c++17 ou posteriormente.

std::create_directory códigos de falha

Implementado P1164 a partir de C++20 incondicionalmente. Isso muda std::create_directory para verificar se o destino já era um diretório em caso de falha. Anteriormente, todos os erros de tipo ERROR_ALREADY_EXISTS eram transformados em códigos bem-sucedidos, mas não criados.

operator<<(std::ostream, nullptr_t)

Por LWG 2221, adicionado operator<<(std::ostream, nullptr_t) para gravação nullptr em streams.

Mais algoritmos paralelos

Novas versões paralelas de is_sorted, is_sorted_until, is_partitioned, set_difference, set_intersection, is_heap, e is_heap_until.

Correções na inicialização atômica

P0883 "Fixando a inicialização atômica" muda std::atomic para inicializar o valor contido T em vez de inicializá-lo por padrão. A correção é ativada ao usar Clang/LLVM com a biblioteca padrão da Microsoft. Ele está atualmente desativado para o compilador Microsoft C++, como uma solução alternativa para um bug no constexpr processamento.

remove_cvref e remove_cvref_t

Implementou as remove_cvref características e remove_cvref_t tipo de P0550. Eles removem a referência e a qualificação cv de um tipo sem funções e matrizes em decomposição para ponteiros (ao contrário std::decay de e std::decay_t).

Macros de teste de recursos

P0941R2 - as macros de teste de recursos estão completas, com suporte para __has_cpp_attribute. As macros de teste de recursos são suportadas em todos os modos padrão.

Proibir agregações com construtores declarados pelo usuário

C++20 P1008R1 - a proibição de agregações com construtores declarados pelo usuário está concluída.

reinterpret_cast constexpr numa função

A reinterpret_cast é ilegal numa constexpr função. O compilador Microsoft C++ anteriormente rejeitaria reinterpret_cast apenas se fosse usado em um constexpr contexto. No Visual Studio 2019, em todos os modos de padrões de linguagem, o compilador diagnostica corretamente um reinterpret_cast na definição de uma constexpr função. O código a seguir agora produz C3615:

long long i = 0;
constexpr void f() {
    int* a = reinterpret_cast<int*>(i); // C3615: constexpr function 'f' cannot result in a constant expression
}

Para evitar o erro, remova o constexpr modificador da declaração de função.

Diagnóstico correto para basic_string construtor de intervalo

No Visual Studio 2019, o basic_string construtor range não suprime mais o diagnóstico do compilador com static_cast. O código a seguir é compilado wchar_t sem avisos no Visual Studio 2017, apesar da possível perda de dados de até ao char inicializar out:

std::wstring ws = /* . . . */;
std::string out(ws.begin(), ws.end()); // VS2019 C4244: 'argument': conversion from 'wchar_t' to 'const _Elem', possible loss of data.

O Visual Studio 2019 gera corretamente o aviso C4244. Para evitar o aviso, você pode inicializar o std::string como mostrado neste exemplo:

std::wstring ws = L"Hello world";
std::string out;
for (wchar_t ch : ws)
{
    out.push_back(static_cast<char>(ch));
}

Chamadas incorretas para += e -= sob /clr ou /ZW agora são detetadas corretamente

Um bug foi introduzido no Visual Studio 2017 que fez com que o compilador ignorasse silenciosamente erros e não gerasse nenhum código para as chamadas inválidas para += e -= sob /clr ou /ZW. O código a seguir é compilado sem erros no Visual Studio 2017, mas no Visual Studio 2019 ele gera corretamente o erro C2845:

public enum class E { e };

void f(System::String ^s)
{
    s += E::e; // in VS2019 C2845: 'System::String ^': pointer arithmetic not allowed on this type.
}

Para evitar o erro neste exemplo, use o += operador com o ToString() método: s += E::e.ToString();.

Inicializadores para membros de dados estáticos embutidos

Os acessos de membros inválidos dentro inline e static constexpr os inicializadores agora são detetados corretamente. O exemplo a seguir compila sem erro no Visual Studio 2017, mas no Visual Studio 2019 no /std:c++17 modo ou posterior ele gera o erro C2248:

struct X
{
    private:
        static inline const int c = 1000;
};

struct Y : X
{
    static inline int d = c; // VS2019 C2248: cannot access private member declared in class 'X'.
};

Para evitar o erro, declare o membro X::c como protegido:

struct X
{
    protected:
        static inline const int c = 1000;
};

C4800 reintegrado

MSVC costumava ter um aviso de desempenho C4800 sobre a conversão implícita para bool. Era muito barulhento e não podia ser suprimido, levando-nos a removê-lo no Visual Studio 2017. No entanto, ao longo do ciclo de vida do Visual Studio 2017, recebemos muitos comentários sobre os casos úteis que ele estava resolvendo. Trazemos de volta no Visual Studio 2019 um C4800 cuidadosamente adaptado, juntamente com o C4165 explicativo. Ambos os avisos são fáceis de suprimir: ou usando um elenco explícito, ou por comparação com 0 do tipo apropriado. C4800 é um aviso de nível 4 desativado por padrão e C4165 é um aviso de nível 3 desativado por padrão. Ambos são detetáveis usando a /Wall opção de compilador.

O exemplo a seguir gera C4800 e C4165 em /Wall:

bool test(IUnknown* p)
{
    bool valid = p; // warning C4800: Implicit conversion from 'IUnknown*' to bool. Possible information loss
    IDispatch* d = nullptr;
    HRESULT hr = p->QueryInterface(__uuidof(IDispatch), reinterpret_cast<void**>(&d));
    return hr; // warning C4165: 'HRESULT' is being converted to 'bool'; are you sure this is what you want?
}

Para evitar os avisos no exemplo anterior, você pode escrever o código assim:

bool test(IUnknown* p)
{
    bool valid = p != nullptr; // OK
    IDispatch* d = nullptr;
    HRESULT hr = p->QueryInterface(__uuidof(IDispatch), reinterpret_cast<void**>(&d));
    return SUCCEEDED(hr);  // OK
}

A função de membro da classe local não tem um corpo

No Visual Studio 2017, o aviso C4822 é gerado somente quando a opção /w14822 do compilador é definida explicitamente. Não é mostrado com /Wall. No Visual Studio 2019, C4822 é um aviso off-by-default , que o torna detetável em /Wall sem ter que definir /w14822 explicitamente.

void example()
{
    struct A
        {
            int boo(); // warning C4822: Local class member function doesn't have a body
        };
}

Corpos de modelo de função contendo if constexpr instruções

No Visual Studio 2019 em /std:c++20 ou /std:c++latest, os corpos de função de modelo que têm if constexpr instruções têm verificações adicionais relacionadas à análise habilitadas. Por exemplo, no Visual Studio 2017, o código a seguir produz C7510 somente se a /permissive- opção estiver definida. No Visual Studio 2019, o mesmo código gera erros mesmo quando a /permissive opção está definida:

// C7510.cpp
// compile using: cl /EHsc /W4 /permissive /std:c++latest C7510.cpp
#include <iostream>

template <typename T>
int f()
{
    T::Type a; // error C7510: 'Type': use of dependent type name must be prefixed with 'typename'
    // To fix the error, add the 'typename' keyword. Use this declaration instead:
    // typename T::Type a;

    if constexpr (a.val)
    {
        return 1;
    }
    else
    {
        return 2;
    }
}

struct X
{
    using Type = X;
    constexpr static int val = 1;
};

int main()
{
    std::cout << f<X>() << "\n";
}

Para evitar o erro, adicione a typename palavra-chave à declaração de a: typename T::Type a;.

O código de assembly embutido não é suportado em uma expressão lambda

A equipe do Microsoft C++ foi recentemente informada de um problema de segurança no qual o uso do inline-assembler dentro de um lambda poderia levar à corrupção de (o registro de endereço de retorno) em tempo de ebp execução. Um invasor mal-intencionado poderia tirar proveito desse cenário. O assembler inline só é suportado em x86, e a interação entre o assembler inline e o resto do compilador é fraca. Tendo em conta estes factos e a natureza do problema, a solução mais segura para este problema foi não permitir o montador em linha dentro de uma expressão lambda.

O único uso de assembler em linha dentro de uma expressão lambda que encontramos "na natureza" foi para capturar o endereço de retorno. Nesse cenário, você pode capturar o endereço de retorno em todas as plataformas simplesmente usando um compilador intrínseco _ReturnAddress().

O código a seguir produz C7553 no Visual Studio 2017 15.9 e versões posteriores do Visual Studio:

#include <cstdio>

int f()
{
    int y = 1724;
    int x = 0xdeadbeef;

    auto lambda = [&]
    {
        __asm {  // C7553: inline assembler is not supported in a lambda

            mov eax, x
            mov y, eax
        }
    };

    lambda();
    return y;
}

Para evitar o erro, mova o código do assembly para uma função nomeada, conforme mostrado no exemplo a seguir:

#include <cstdio>

void g(int& x, int& y)
{
    __asm {
        mov eax, x
        mov y, eax
    }
}

int f()
{
    int y = 1724;
    int x = 0xdeadbeef;
    auto lambda = [&]
    {
        g(x, y);
    };
    lambda();
    return y;
}

int main()
{
    std::printf("%d\n", f());
}

Depuração do iterador e std::move_iterator

O recurso de depuração do iterador foi ensinado a desembrulhar std::move_iteratorcorretamente. Por exemplo, std::copy(std::move_iterator<std::vector<int>::iterator>, std::move_iterator<std::vector<int>::iterator>, int*) agora pode engajar o memcpy caminho rápido.

Correções para <a imposição de palavras-chave xkeycheck.h>

A imposição da biblioteca padrão em <xkeycheck.h> para macros que substituem uma palavra-chave foi corrigida. A biblioteca agora emite a palavra-chave do problema real detetada em vez de uma mensagem genérica. Ele também suporta palavras-chave C++20 e evita enganar o IntelliSense para dizer que palavras-chave aleatórias são macros.

Os tipos de alocador não foram mais preteridos

std::allocator<void>, std::allocator::size_typee std::allocator::difference_type não são mais preteridos.

Aviso correto para estreitar conversões de cadeia de caracteres

Removido um espúrio static_cast do que não era exigido pelo padrão, e que acidentalmente suprimiu avisos de std::string estreitamento C4244. Tentativas de chamar std::string::string(const wchar_t*, const wchar_t*) agora corretamente emitir C4244 sobre estreitar um wchar_t em um char.

Várias correções para a <correção do sistema> de arquivos

  • Corrigida std::filesystem::last_write_time a falha ao tentar alterar o último tempo de gravação de um diretório.
  • O std::filesystem::directory_entry construtor agora armazena um resultado com falha, em vez de lançar uma exceção, quando fornecido um caminho de destino inexistente.
  • A std::filesystem::create_directory versão de 2 parâmetros foi alterada para chamar a versão de 1 parâmetro, como a função subjacente CreateDirectoryExW usaria copy_symlink quando o existing_p era um link simbólico.
  • std::filesystem::directory_iterator não falha mais quando um link simbólico quebrado é encontrado.
  • std::filesystem::space agora aceita caminhos relativos.
  • std::filesystem::path::lexically_relative já não se confunde com as barras rasteiras, reportadas como LWG 3096.
  • Trabalhou em torno CreateSymbolicLinkW da rejeição de caminhos com barras para a frente em std::filesystem::create_symlink.
  • Funcionou em torno da função de modo delete de exclusão POSIX que existia no Windows 10 LTSB 1609, mas não podia realmente excluir arquivos.
  • Os std::boyer_moore_searcher construtores e std::boyer_moore_horspool_searcher operadores de atribuição de cópia e cópia agora realmente copiam as coisas.

Algoritmos paralelos no Windows 8 e versões posteriores

A biblioteca de algoritmos paralelos agora usa corretamente a família real WaitOnAddress no Windows 8 e posterior, em vez de sempre usar o Windows 7 e versões falsas anteriores.

std::system_category::message() espaço em branco

std::system_category::message() agora corta o espaço em branco à direita da mensagem retornada.

std::linear_congruential_engine dividir por zero

Algumas condições que causariam std::linear_congruential_engine a divisão por 0 foram corrigidas.

Correções para desempacotamento do iterador

Algumas máquinas de desempacotamento de iterador foram expostas pela primeira vez para integração programador-usuário no Visual Studio 2017 15.8. Foi descrito no artigo do C++ Team Blog STL Features and Fixes in VS 2017 15.8. Esse maquinário não desembrulha mais iteradores derivados de iteradores de biblioteca padrão. Por exemplo, um usuário que deriva e std::vector<int>::iterator tenta personalizar o comportamento agora obtém seu comportamento personalizado ao chamar algoritmos de biblioteca padrão, em vez do comportamento de um ponteiro.

A função de contêiner reserve não ordenado agora realmente reserva para elementos N, conforme descrito em LWG 2156.

Tratamento do tempo

  • Anteriormente, alguns valores de tempo que eram passados para a biblioteca de simultaneidade estouravam, por exemplo, condition_variable::wait_for(seconds::max()). Agora corrigidos, os estouros mudaram o comportamento em um ciclo aparentemente aleatório de 29 dias (quando uint32_t milissegundos aceitos pelas APIs subjacentes do Win32 transbordaram).

  • O <cabeçalho ctime> agora declara timespec corretamente e timespec_get no namespace std, e também os declara no namespace global.

Várias correções para contêineres

  • Muitas funções de contêiner interno de biblioteca padrão foram criadas private para uma experiência IntelliSense aprimorada. Mais correções para marcar membros, como private são esperados em versões posteriores do MSVC.

  • Corrigimos problemas de correção de segurança de exceção que faziam com que contêineres baseados em nó, como list, mape unordered_map, ficassem corrompidos. Durante uma propagate_on_container_copy_assignment operação ou propagate_on_container_move_assignment operação de reatribuição, liberávamos o nó sentinela do contêiner com o alocador antigo, fazíamos a atribuição POCCA/POCMA sobre o alocador antigo e, em seguida, tentávamos adquirir o nó sentinela do novo alocador. Se essa alocação falhou, o contêiner foi corrompido. Ele nem poderia ser destruído, pois possuir um nó sentinela é uma estrutura de dados rígida invariante. Esse código foi corrigido para criar o novo nó sentinela usando o alocador do contêiner de origem antes de destruir o nó sentinela existente.

  • Os contêineres foram fixados para sempre copiar/mover/trocar alocadores de acordo com propagate_on_container_copy_assignment, propagate_on_container_move_assignmente propagate_on_container_swap, mesmo para alocadores declarados is_always_equal.

  • Adicionadas as sobrecargas para mesclagem de contêiner e funções de membro de extração que aceitam contêineres rvalue. Para obter mais informações, consulte P0083 "Emendando mapas e conjuntos"

std::basic_istream::read processamento de \r\n =>\n

std::basic_istream::read foi corrigido para não gravar em partes do buffer fornecido temporariamente como parte do \r\n\n processamento. Essa alteração abre mão de parte da vantagem de desempenho que foi obtida no Visual Studio 2017 15.8 para leituras maiores que 4K em tamanho. No entanto, as melhorias de eficiência ao evitar três chamadas virtuais por personagem ainda estão presentes.

std::bitset construtor

O std::bitset construtor não lê mais os uns e zeros na ordem inversa para grandes conjuntos de bits.

std::pair::operator= regressão

Corrigimos uma regressão no operador de atribuição introduzida std::pair ao implementar o LWG 2729 "Missing SFINAE on std::pair::operator=";. Ele agora aceita corretamente tipos conversíveis para std::pair novamente.

Contextos não deduzidos para add_const_t

Corrigimos um pequeno bug de características de tipo, onde add_const_t e funções relacionadas devem ser um contexto não deduzido. Em outras palavras, add_const_t deve ser um pseudônimo para typename add_const<T>::type, não const T.

Melhorias de conformidade em 16.1

char8_t

P0482r6. C++20 adiciona um novo tipo de caractere que é usado para representar unidades de código UTF-8. u8 literais de cadeia de caracteres em C++20 têm tipo const char8_t[N] em vez de const char[N], o que era o caso anteriormente. Foram propostas alterações semelhantes para a norma C na N2231. Sugestões para char8_t correção de compatibilidade com versões anteriores são dadas em P1423r3. O compilador Microsoft C++ adiciona suporte para char8_t no Visual Studio 2019 versão 16.1 quando você especifica a opção do /Zc:char8_t compilador. Ele pode ser revertido para o comportamento C++17 via /Zc:char8_t-. O compilador EDG que alimenta o IntelliSense ainda não o suporta no Visual Studio 2019 versão 16.1. Você pode ver erros espúrios somente do IntelliSense que não afetam a compilação real.

Exemplo

const char* s = u8"Hello"; // C++17
const char8_t* s = u8"Hello"; // C++20

std::type_identity Metafunção e std::identity objeto de função

P0887R1 type_identity. A extensão de modelo de classe preterida std::identity foi removida e substituída pela metafunção C++20 std::type_identity e std::identity objeto de função. Ambos estão disponíveis apenas em /std:c++latest (/std:c++20 no Visual Studio 2019 versão 16.11 e posterior).

O exemplo a seguir produz aviso de substituição C4996 para std::identity (definido em <type_traits>) no Visual Studio 2017:

#include <type_traits>

using T = std::identity<int>::type;
T x, y = std::identity<T>{}(x);
int i = 42;
long j = std::identity<long>{}(i);

O exemplo a seguir mostra como usar o novo std::identity (definido em <funcional>) junto com o novo std::type_identity:

#include <type_traits>
#include <functional>

using T = std::type_identity<int>::type;
T x, y = std::identity{}(x);
int i = 42;
long j = static_cast<long>(i);

Verificações de sintaxe para lambdas genéricas

O novo processador lambda permite algumas verificações sintáticas de modo de conformidade em lambdas genéricas, em /std:c++latest (/std:c++20 no Visual Studio 2019 versão 16.11 e posterior) ou em qualquer outro modo de linguagem com /Zc:lambda no Visual Studio 2019 versão 16.9 ou posterior (anteriormente disponível como /experimental:newLambdaProcessor início no Visual Studio 2019 versão 16.3).

O processador lambda herdado compila este exemplo sem avisos, mas o novo processador lambda produz o erro C2760:

void f() {
    auto a = [](auto arg) {
        decltype(arg)::Type t; // C2760 syntax error: unexpected token 'identifier', expected ';'
    };
}

Este exemplo mostra a sintaxe correta, agora imposta pelo compilador:

void f() {
    auto a = [](auto arg) {
        typename decltype(arg)::Type t;
    };
}

Pesquisa dependente de argumentos para chamadas de função

P0846R0 (C++20) Maior capacidade de encontrar modelos de função por meio de pesquisa dependente de argumentos para expressões de chamada de função com argumentos de modelo explícitos. Requer /std:c++latest (ou /std:c++20 no Visual Studio 2019 versão 16.11 e posterior).

Inicialização designada

P0329R4 (C++20) A inicialização designada permite que membros específicos sejam selecionados na inicialização agregada usando a Type t { .member = expr } sintaxe. Requer /std:c++latest (ou /std:c++20 no Visual Studio 2019 versão 16.11 e posterior).

Classificação da conversão de enum para o seu tipo subjacente fixo

O compilador agora classifica as conversões de enum de acordo com N4800 11.3.3.2 Classificação de sequências de conversão implícitas (4.2):

  • Uma conversão que promove uma enumeração cujo tipo subjacente é fixado ao seu tipo subjacente é melhor do que uma que promove para o tipo subjacente promovido, se os dois forem diferentes.

Essa classificação de conversão não foi implementada corretamente antes do Visual Studio 2019 versão 16.1. O comportamento de conformidade pode alterar o comportamento de resolução de sobrecarga ou expor uma ambiguidade onde não foi detetado anteriormente.

Essa alteração de comportamento do compilador se aplica a todos os /std modos e é uma alteração de quebra binária e de origem.

O exemplo a seguir demonstra como o comportamento do compilador muda na versão 16.1 e versões posteriores:

#include <type_traits>

enum E : unsigned char { e };

int f(unsigned int)
{
    return 1;
}

int f(unsigned char)
{
    return 2;
}

struct A {};
struct B : public A {};

int f(unsigned int, const B&)
{
    return 3;
}

int f(unsigned char, const A&)
{
    return 4;
}

int main()
{
    // Calls f(unsigned char) in 16.1 and later. Called f(unsigned int) in earlier versions.
    // The conversion from 'E' to the fixed underlying type 'unsigned char' is better than the
    // conversion from 'E' to the promoted type 'unsigned int'.
    f(e);
  
    // Error C2666. This call is ambiguous, but previously called f(unsigned int, const B&). 
    f(e, B{});
}

Funções de biblioteca padrão novas e atualizadas (C++20)

  • starts_with() e ends_with() para basic_string e basic_string_view.
  • contains() para contentores associativos.
  • remove(), remove_if(), e unique() para list e forward_list agora voltar size_type.
  • shift_left() e shift_right() adicionado ao <algoritmo>.

Melhorias de conformidade em 16.2

noexcept constexpr funções

constexpr As funções não são mais consideradas noexcept por padrão quando usadas em uma expressão constante. Essa alteração de comportamento vem da resolução do CWG 1351 do Core Working Group (CWG) e está habilitada no /permissive-. O exemplo a seguir compila no Visual Studio 2019 versão 16.1 e anterior, mas produz C2338 no Visual Studio 2019 versão 16.2:

constexpr int f() { return 0; }

int main() {
    static_assert(noexcept(f()), "f should be noexcept"); // C2338 in 16.2
}

Para corrigir o erro, adicione a noexcept expressão à declaração de função:

constexpr int f() noexcept { return 0; }

int main() {
    static_assert(noexcept(f()), "f should be noexcept");
}

Expressões binárias com diferentes tipos de enum

C++20 preteriu as conversões aritméticas usuais em operandos, onde:

  • Um operando é do tipo enumeração, e

  • o outro é de um tipo de enumeração diferente ou um tipo de vírgula flutuante.

Para obter mais informações, consulte P1120R0.

No Visual Studio 2019 versão 16.2 e posterior, o código a seguir produz um aviso C5054 de nível 4 quando a opção do compilador está habilitada /std:c++latest (/std:c++20 no Visual Studio 2019 versão 16.11 e posterior):

enum E1 { a };
enum E2 { b };
int main() {
    int i = a | b; // warning C5054: operator '|': deprecated between enumerations of different types
}

Para evitar o aviso, use static_cast para converter o segundo operando:

enum E1 { a };
enum E2 { b };
int main() {
  int i = a | static_cast<int>(b);
}

Usar uma operação binária entre uma enumeração e um tipo de ponto flutuante agora é um aviso C5055 de nível 1 quando a opção do compilador está habilitada /std:c++latest (/std:c++20 no Visual Studio 2019 versão 16.11 e posterior):

enum E1 { a };
int main() {
  double i = a * 1.1;
}

Para evitar o aviso, use static_cast para converter o segundo operando:

enum E1 { a };
int main() {
   double i = static_cast<int>(a) * 1.1;
}

Igualdade e comparações relacionais de matrizes

Igualdade e comparações relacionais entre dois operandos do tipo array são preteridas em C++20 (P1120R0). Em outras palavras, uma operação de comparação entre duas matrizes (apesar das semelhanças de classificação e extensão) é agora um aviso. No Visual Studio 2019 versão 16.2 e posterior, o código a seguir produz aviso de nível 1 C5056 quando a opção do compilador está habilitada /std:c++latest (/std:c++20 no Visual Studio 2019 versão 16.11 e posterior):

int main() {
    int a[] = { 1, 2, 3 };
    int b[] = { 1, 2, 3 };
    if (a == b) { return 1; } // warning C5056: operator '==': deprecated for array types
}

Para evitar o aviso, você pode comparar os endereços dos primeiros elementos:

int main() {
    int a[] = { 1, 2, 3 };
    int b[] = { 1, 2, 3 };
    if (&a[0] == &b[0]) { return 1; }
}

Para determinar se o conteúdo de duas matrizes é igual, use a std::equal função:

std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));

Efeito da definição de operador de nave espacial em == e !=

Uma definição do operador da nave espacial (<=>) por si só não irá mais reescrever expressões envolvendo == ou != a menos que o operador da nave espacial esteja marcado como = default (P1185R2). O exemplo a seguir compila no Visual Studio 2019 RTW e versão 16.1, mas produz C2678 no Visual Studio 2019 versão 16.2:

#include <compare>

struct S {
  int a;
  auto operator<=>(const S& rhs) const {
    return a <=> rhs.a;
  }
};
bool eq(const S& lhs, const S& rhs) {
  return lhs == rhs; // error C2676
}
bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs; // error C2676
}

Para evitar o erro, defina-o operator== ou declare-o como padrão:

#include <compare>

struct S {
  int a;
  auto operator<=>(const S& rhs) const {
    return a <=> rhs.a;
  }
  bool operator==(const S&) const = default;
};
bool eq(const S& lhs, const S& rhs) {
  return lhs == rhs;
}
bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs;
}

Melhorias na biblioteca padrão

  • <charconv>to_chars() com precisão fixa/científica. (A precisão geral está atualmente prevista para 16.4.)
  • P0020R6: atomic<float>, atomic<double>, atomic<long double>
  • P0463R1: Endian
  • P0482R6: Suporte de biblioteca para char8_t
  • P0600R1: [[nodiscard]] Para o STL, Parte 1
  • P0653R2: to_address()
  • P0754R2: <versão>
  • P0771R1: noexcept For std::function's move construtor

Comparadores Const para contentores associativos

Código para pesquisa e inserção em set, map, multisete multimap foi mesclado para tamanho de código reduzido. As operações de inserção agora chamam a comparação menor do que em um const functor de comparação, da mesma forma que as operações de pesquisa fizeram anteriormente. O código a seguir é compilado no Visual Studio 2019 versão 16.1 e anterior, mas gera C3848 no Visual Studio 2019 versão 16.2:

#include <iostream>
#include <map>

using namespace std;

struct K
{
   int a;
   string b = "label";
};

struct Comparer  {
   bool operator() (K a, K b) {
      return a.a < b.a;
   }
};

map<K, double, Comparer> m;

K const s1{1};
K const s2{2};
K const s3{3};

int main() {

   m.emplace(s1, 1.08);
   m.emplace(s2, 3.14);
   m.emplace(s3, 5.21);

}

Para evitar o erro, faça o operador constde comparação :

struct Comparer  {
   bool operator() (K a, K b) const {
      return a.a < b.a;
   }
};

Melhorias de conformidade no Visual Studio 2019 versão 16.3

Operadores de extração de fluxo para char* remoção

Os operadores de extração de fluxo para ponteiro para caracteres foram removidos e substituídos por operadores de extração para matriz de caracteres (por P0487R1). O GT21 considera que as sobrecargas removidas não são seguras. No /std:c++20 modo ou /std:c++latest , o exemplo a seguir agora produz C2679:

// stream_extraction.cpp
// compile by using: cl /std:c++latest stream_extraction.cpp

#include <iostream>
#include <iomanip>

int main() {
    char x[42];
    char* p = x;
    std::cin >> std::setw(42);
    std::cin >> p;  // C2679: binary '>>': no operator found which takes a right-hand operand of type 'char *' (or there is no acceptable conversion)
}

Para evitar o erro, use o operador de extração com uma char[] variável:

#include <iostream>
#include <iomanip>

int main() {
    char x[42];
    std::cin >> std::setw(42);
    std::cin >> x;  // OK
}

Novas palavras-chave requires e concept

Novas palavras-chave requires e concept foram adicionadas ao compilador Microsoft C++. Se você tentar usar um como um identificador no /std:c++20 ou /std:c++latest modo, o compilador aciona C2059 para indicar um erro de sintaxe.

Construtores como nomes de tipo não permitidos

O compilador não considera mais nomes de construtores como nomes de classe injetados neste caso: quando eles aparecem em um nome qualificado após um alias para uma especialização de modelo de classe. Anteriormente, os construtores eram utilizáveis como um nome de tipo para declarar outras entidades. O exemplo a seguir agora produz C3646:

#include <chrono>

class Foo {
   std::chrono::milliseconds::duration TotalDuration{}; // C3646: 'TotalDuration': unknown override specifier
};

Para evitar o erro, declare TotalDuration como mostrado aqui:

#include <chrono>

class Foo {
  std::chrono::milliseconds TotalDuration {};
};

Controlo mais rigoroso das extern "C" funções

Se uma extern "C" função foi declarada em namespaces diferentes, as versões anteriores do compilador Microsoft C++ não verificavam se as declarações eram compatíveis. No Visual Studio 2019 versão 16.3 e posterior, o compilador verifica a compatibilidade. No /permissive- modo, o código a seguir produz erros C2371 e C2733:

using BOOL = int;

namespace N
{
   extern "C" void f(int, int, int, bool);
}

void g()
{
   N::f(0, 1, 2, false);
}

extern "C" void f(int, int, int, BOOL){}
    // C2116: 'N::f': function parameter lists do not match between declarations
    // C2733: 'f': you cannot overload a function with 'extern "C"' linkage

Para evitar os erros no exemplo anterior, use bool em vez de BOOL consistentemente em ambas as declarações de f.

Melhorias na biblioteca padrão

Os cabeçalhos <não padrão stdexcpt.h> e <typeinfo.h> foram removidos. O código que os inclui deve, em vez disso, incluir a exceção< de cabeçalhos >padrão e <typeinfo>, respectivamente.

Melhorias de conformidade no Visual Studio 2019 versão 16.4

Melhor aplicação da pesquisa de nomes em duas fases para ids qualificadas em /permissive-

A pesquisa de nomes em duas fases requer que os nomes não dependentes usados nos corpos do modelo estejam visíveis para o modelo no momento da definição. Anteriormente, esses nomes podem ter sido encontrados quando o modelo é instanciado. Esta alteração torna mais fácil escrever código portátil e em conformidade no MSVC sob a /permissive- bandeira.

No Visual Studio 2019 versão 16.4 com o /permissive- sinalizador definido, o exemplo a seguir produz um erro, porque N::f não é visível quando o f<T> modelo é definido:

template <class T>
int f() {
    return N::f() + T{}; // error C2039: 'f': is not a member of 'N'
}

namespace N {
    int f() { return 42; }
}

Normalmente, esse erro pode ser corrigido incluindo cabeçalhos ausentes ou funções ou variáveis de declaração de encaminhamento, conforme mostrado no exemplo a seguir:

namespace N {
    int f();
}

template <class T>
int f() {
    return N::f() + T{};
}

namespace N {
    int f() { return 42; }
}

Conversão implícita de expressões constantes integrais em ponteiro nulo

O compilador MSVC agora implementa o CWG Issue 903 no modo de conformidade (/permissive-). Esta regra não permite a conversão implícita de expressões constantes integrais (exceto para o literal inteiro '0') em constantes de ponteiro nulas. O exemplo a seguir produz C2440 no modo de conformidade:

int* f(bool* p) {
    p = false; // error C2440: '=': cannot convert from 'bool' to 'bool *'
    p = 0; // OK
    return false; // error C2440: 'return': cannot convert from 'bool' to 'int *'
}

Para corrigir o erro, use nullptr em vez de false. Um literal 0 ainda é permitido:

int* f(bool* p) {
    p = nullptr; // OK
    p = 0; // OK
    return nullptr; // OK
}

Regras padrão para tipos de literais inteiros

No modo de conformidade (ativado pela /permissive-), o MSVC usa as regras padrão para tipos de literais inteiros. Literais decimais muito grandes para caber em um signed int tipo unsigned intpreviamente dado. Agora, tais literais recebem o próximo maior signed tipo inteiro, long long. Além disso, literais com o sufixo 'll' que são muito grandes para caber em um signed tipo são dados tipo unsigned long long.

Essa alteração pode levar a diferentes diagnósticos de aviso sendo gerados e diferenças de comportamento para operações aritméticas em literais.

O exemplo a seguir mostra o novo comportamento no Visual Studio 2019 versão 16.4. A i variável agora é do tipo unsigned int, então o aviso é levantado. Os bits de alta ordem da variável j são definidos como 0.

void f(int r) {
    int i = 2964557531; // warning C4309: truncation of constant value
    long long j = 0x8000000000000000ll >> r; // literal is now unsigned, shift will fill high-order bits with 0
}

O exemplo a seguir demonstra como manter o comportamento antigo e evitar os avisos e a alteração de comportamento em tempo de execução:

void f(int r) {
int i = 2964557531u; // OK
long long j = (long long)0x8000000000000000ll >> r; // shift will keep high-order bits
}

Parâmetros de função que sombreiam os parâmetros do modelo

O compilador MSVC agora gera um erro quando um parâmetro de função sombreia um parâmetro de modelo:

template<typename T>
void f(T* buffer, int size, int& size_read);

template<typename T, int Size>
void f(T(&buffer)[Size], int& Size) // error C7576: declaration of 'Size' shadows a template parameter
{
    return f(buffer, Size, Size);
}

Para corrigir o erro, altere o nome de um dos parâmetros:

template<typename T>
void f(T* buffer, int size, int& size_read);

template<typename T, int Size>
void f(T (&buffer)[Size], int& size_read)
{
    return f(buffer, Size, size_read);
}

Especializações fornecidas pelo usuário de características de tipo

Em conformidade com a subcláusula meta.rqmts do Standard, o compilador MSVC agora gera um erro quando encontra uma especialização definida pelo usuário de um dos modelos especificados type_traits no std namespace. A menos que especificado de outra forma, tais especializações resultam em comportamento indefinido. O exemplo a seguir tem um comportamento indefinido porque viola a regra e falha com o static_assert erro C2338.

#include <type_traits>
struct S;

template<>
struct std::is_fundamental<S> : std::true_type {};

static_assert(std::is_fundamental<S>::value, "fail");

Para evitar o erro, defina uma struct que herde da preferida type_traite especialize que:

#include <type_traits>

struct S;

template<typename T>
struct my_is_fundamental : std::is_fundamental<T> {};

template<>
struct my_is_fundamental<S> : std::true_type { };

static_assert(my_is_fundamental<S>::value, "fail");

Alterações nos operadores de comparação fornecidos pelo compilador

O compilador MSVC agora implementa as seguintes alterações nos operadores de comparação por P1630R1 quando a /std:c++20 opção ou /std:c++latest está habilitada:

O compilador não reescreve mais expressões usando operator== se elas envolverem um tipo de retorno que não seja um boolarquivo . O código a seguir agora produz o erro C2088:

struct U {
    operator bool() const;
};

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

bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs;  // C2088: '!=': illegal for struct
}

Para evitar o erro, você deve definir explicitamente o operador necessário:

struct U {
    operator bool() const;
};

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

bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs;
}

O compilador não define mais um operador de comparação padrão se ele for membro de uma classe semelhante a uma união. O exemplo a seguir agora produz o erro C2120:

#include <compare>

union S {
    int a;
    char b;
    auto operator<=>(const S&) const = default;
};

bool lt(const S& lhs, const S& rhs) {
    return lhs < rhs;
}

Para evitar o erro, defina um corpo para o operador:

#include <compare>

union S {
    int a;
    char b;
    auto operator<=>(const S&) const { ... }
};

bool lt(const S& lhs, const S& rhs) {
    return lhs < rhs;
}

O compilador não definirá mais um operador de comparação padrão se a classe contiver um membro de referência. O código a seguir agora produz o erro C2120:

#include <compare>

struct U {
    int& a;
    auto operator<=>(const U&) const = default;
};

bool lt(const U& lhs, const U& rhs) {
    return lhs < rhs;
}

Para evitar o erro, defina um corpo para o operador:

#include <compare>

struct U {
    int& a;
    auto operator<=>(const U&) const { ... };
};

bool lt(const U& lhs, const U& rhs) {
    return lhs < rhs;
}

Melhorias de conformidade no Visual Studio 2019 versão 16.5

Declaração de especialização explícita sem um inicializador não é uma definição

No /permissive-, o MSVC agora impõe uma regra padrão de que declarações de especialização explícitas sem inicializadores não são definições. Anteriormente, a declaração seria considerada uma definição com um inicializador padrão. O efeito é observável no tempo do link, uma vez que um programa dependendo desse comportamento pode agora ter símbolos não resolvidos. Este exemplo agora resulta em um erro:

template <typename> struct S {
    static int a;
};

// In permissive-, this declaration isn't a definition, and the program won't link.
template <> int S<char>::a;

int main() {
    return S<char>::a;
}
error LNK2019: unresolved external symbol "public: static int S<char>::a" (?a@?$S@D@@2HA) referenced in function _main at link time.

Para resolver o problema, adicione um inicializador:

template <typename> struct S {
    static int a;
};

// Add an initializer for the declaration to be a definition.
template <> int S<char>::a{};

int main() {
    return S<char>::a;
}

A saída do pré-processador preserva novas linhas

O pré-processador experimental agora preserva novas linhas e espaço em branco ao usar /P ou /E com /experimental:preprocessoro .

Tendo em conta este exemplo de fonte,

#define m()
line m(
) line

A saída anterior do /E foi:

line line
#line 2

A nova saída do /E é agora:

line
 line

import e module as palavras-chave dependem do contexto

Por P1857R1, import e module as diretivas de pré-processador têm novas restrições em sua sintaxe. Este exemplo não compila mais:

import // Invalid
m;     // error C2146: syntax error: missing ';' before identifier 'm'

Para resolver o problema, mantenha a importação na mesma linha:

import m; // OK

Remoção de std::weak_equality e std::strong_equality

A mesclagem de P1959R0 requer que o compilador remova o comportamento e as referências aos std::weak_equality tipos e std::strong_equality .

O código neste exemplo não compila mais:

#include <compare>

struct S {
    std::strong_equality operator<=>(const S&) const = default;
};

void f() {
    nullptr<=>nullptr;
    &f <=> &f;
    &S::operator<=> <=> &S::operator<=>;
}

O exemplo agora leva a estes erros:

error C2039: 'strong_equality': is not a member of 'std'
error C2143: syntax error: missing ';' before '<=>'
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C7546: binary operator '<=>': unsupported operand types 'nullptr' and 'nullptr'
error C7546: binary operator '<=>': unsupported operand types 'void (__cdecl *)(void)' and 'void (__cdecl *)(void)'
error C7546: binary operator '<=>': unsupported operand types 'int (__thiscall S::* )(const S &) const' and 'int (__thiscall S::* )(const S &) const'

Para resolver o problema, atualize para preferir os operadores relacionais internos e substitua os tipos removidos:

#include <compare>

struct S {
    std::strong_ordering operator<=>(const S&) const = default; // prefer 'std::strong_ordering'
};

void f() {
    nullptr != nullptr; // use pre-existing builtin operator != or ==.
    &f != &f;
    &S::operator<=> != &S::operator<=>;
}

Alterações no TLS Guard

Anteriormente, as variáveis de thread-local em DLLs não eram inicializadas corretamente. Além do thread que carregou a DLL, eles não foram inicializados antes do primeiro uso em threads que existiam antes da DLL ser carregada. Este defeito foi agora corrigido. As variáveis de thread-local em tal DLL são inicializadas imediatamente antes de seu primeiro uso em tais threads.

Esse novo comportamento de teste de inicialização em usos de variáveis thread-local pode ser desabilitado usando a /Zc:tlsGuards- opção de compilador. Ou, adicionando o [[msvc::no_tls_guard]] atributo a variáveis locais de thread específicas.

Melhor diagnóstico de chamada para funções excluídas

Nosso compilador foi mais permissivo sobre chamadas para funções excluídas anteriormente. Por exemplo, se as chamadas acontecessem no contexto de um corpo de modelo, não diagnosticaríamos a chamada. Além disso, se houvesse várias instâncias de chamadas para funções excluídas, emitiríamos apenas um diagnóstico. Agora emitimos um diagnóstico para cada um deles.

Uma consequência do novo comportamento pode produzir uma pequena alteração: o código que chamou uma função excluída não seria diagnosticado se nunca fosse necessário para a geração de código. Agora diagnosticamos isso lá na frente.

Este exemplo mostra o código que agora produz um erro:

struct S {
  S() = delete;
  S(int) { }
};

struct U {
  U() = delete;
  U(int i): s{ i } { }

  S s{};
};

U u{ 0 };
error C2280: 'S::S(void)': attempting to reference a deleted function
note: see declaration of 'S::S'
note: 'S::S(void)': function was explicitly deleted

Para resolver o problema, remova as chamadas para funções excluídas:

struct S {
  S() = delete;
  S(int) { }
};

struct U {
  U() = delete;
  U(int i): s{ i } { }

  S s;  // Do not call the deleted ctor of 'S'.
};

U u{ 0 };

Melhorias de conformidade no Visual Studio 2019 versão 16.6

Os fluxos de biblioteca padrão rejeitam inserções de tipos de caracteres codificados incorretamente

Tradicionalmente, inserir a wchar_t em um std::ostream, e inserir char16_t ou char32_t em um std::ostream ou std::wostream, produz seu valor integral. A inserção de ponteiros nesses tipos de caracteres gera o valor do ponteiro. Os programadores não acham nenhum dos casos intuitivo. Eles geralmente esperam que a biblioteca padrão transcodifice o caractere ou a cadeia de caracteres terminada em nulo e produza o resultado.

A proposta C++20 P1423R3 adiciona sobrecargas de operador de inserção de fluxo excluídas para essas combinações de fluxos e tipos de ponteiro de caracteres ou caracteres. Sob /std:c++20 ou /std:c++latest, as sobrecargas tornam essas inserções mal formadas, em vez de se comportarem de maneira provavelmente não intencional. O compilador gera o erro C2280 quando um é encontrado. Você pode definir a macro _HAS_STREAM_INSERTION_OPERATORS_DELETED_IN_CXX20 "escape hatch" para 1 restaurar o comportamento antigo. (A proposta suprime igualmente os operadores de inserção de fluxos para char8_t. Nossa biblioteca padrão implementou sobrecargas semelhantes quando adicionamos char8_t suporte, portanto, o comportamento "errado" nunca esteve disponível para char8_t.)

Este exemplo mostra o comportamento com essa alteração:

#include <iostream>
int main() {
    const wchar_t cw = L'x', *pw = L"meow";
    const char16_t c16 = u'x', *p16 = u"meow";
    const char32_t c32 = U'x', *p32 = U"meow";
    std::cout << cw << ' ' << pw << '\n';
    std::cout << c16 << ' ' << p16 << '\n';
    std::cout << c32 << ' ' << p32 << '\n';
    std::wcout << c16 << ' ' << p16 << '\n';
    std::wcout << c32 << ' ' << p32 << '\n';
}

O código agora produz estas mensagens de diagnóstico:

error C2280: 'std::basic_ostream<char,std::char_traits<char>> &std::<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,wchar_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<char,std::char_traits<char>> &std::<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char16_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<char,std::char_traits<char>> &std::<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char32_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::<<<std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,char16_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::<<<std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,char32_t)': attempting to reference a deleted function

Você pode obter o efeito do comportamento antigo em todos os modos de idioma convertendo tipos de caractere em unsigned int, ou tipos de ponteiro para caractere em const void*:

#include <iostream>
int main() {
    const wchar_t cw = L'x', *pw = L"meow";
    const char16_t c16 = u'x', *p16 = u"meow";
    const char32_t c32 = U'x', *p32 = U"meow";
    std::cout << (unsigned)cw << ' ' << (const void*)pw << '\n'; // Outputs "120 0052B1C0"
    std::cout << (unsigned)c16 << ' ' << (const void*)p16 << '\n'; // Outputs "120 0052B1CC"
    std::cout << (unsigned)c32 << ' ' << (const void*)p32 << '\n'; // Outputs "120 0052B1D8"
    std::wcout << (unsigned)c16 << ' ' << (const void*)p16 << '\n'; // Outputs "120 0052B1CC"
    std::wcout << (unsigned)c32 << ' ' << (const void*)p32 << '\n'; // Outputs "120 0052B1D8"
}

Tipo de retorno alterado de std::pow() para std::complex

Anteriormente, a implementação MSVC das regras de promoção para o tipo de retorno do modelo std::pow() de função estava incorreta. Por exemplo, devolvido pow(complex<float>, int)anteriormentecomplex<float>. Agora ele retorna complex<double>corretamente. A correção foi implementada incondicionalmente para todos os modos de padrões no Visual Studio 2019 versão 16.6.

Essa alteração pode causar erros do compilador. Por exemplo, anteriormente você podia multiplicar pow(complex<float>, int) por um floatarquivo . Como complex<T> operator* espera argumentos do mesmo tipo, o exemplo a seguir agora emite o erro de compilador C2676:

// pow_error.cpp
// compile by using: cl /EHsc /nologo /W4 pow_error.cpp
#include <complex>

int main() {
    std::complex<float> cf(2.0f, 0.0f);
    (void) (std::pow(cf, -1) * 3.0f);
}
pow_error.cpp(7): error C2676: binary '*': 'std::complex<double>' does not define this operator or a conversion to a type acceptable to the predefined operator

Há muitas correções possíveis:

  • Altere o tipo do float multiplicando para double. Esse argumento pode ser convertido diretamente em um complex<double> para corresponder ao tipo retornado por pow.

  • Restrinja o resultado de pow para complex<float> dizendo complex<float>{pow(ARG, ARG)}. Em seguida, você pode continuar a multiplicar por um float valor.

  • Passe float em vez de int para pow. Esta operação pode ser mais lenta.

  • Em alguns casos, você pode evitar pow completamente. Por exemplo, pow(cf, -1) pode ser substituído por divisão.

switch advertências para C

No Visual Studio 2019 versão 16.6 e posterior, o compilador implementa alguns avisos C++ preexistentes para código compilado como C. Os seguintes avisos agora estão habilitados em diferentes níveis: C4060, C4061, C4062, C4063, C4064, C4065, C4808 e C4809. Os avisos C4065 e C4060 estão desativados por padrão em C.

Os avisos são acionados em instruções ausentes case , indefinidas enume instruções de switch incorretas bool (ou seja, aquelas que contêm muitos casos). Por exemplo:

#include <stdbool.h>

int main() {
    bool b = true;
    switch (b) {
        case true: break;
        case false: break;
        default: break; // C4809: switch statement has redundant 'default' label;
                        // all possible 'case' labels are given
    }
}

Para corrigir esse código, remova o caso redundante default :

#include <stdbool.h>

int main() {
    bool b = true;
    switch (b) {
        case true: break;
        case false: break;
    }
}

Classes sem nome em typedef declarações

No Visual Studio 2019 versão 16.6 e posterior, o comportamento das typedef declarações foi restrito para estar em conformidade com P1766R1. Com esta atualização, as classes sem nome dentro de uma typedef declaração não podem ter outros membros além de:

  • membros de dados não estáticos sem inicializadores de membros padrão,
  • classes de membros, ou
  • enumerações de membros.

As mesmas restrições são aplicadas recursivamente a cada classe aninhada. A restrição destina-se a garantir a simplicidade das estruturas que têm typedef nomes para fins de ligação. Eles devem ser simples o suficiente para que nenhum cálculo de vinculação seja necessário antes que o compilador chegue ao typedef nome para vinculação.

Essa alteração afeta todos os modos de padrões do compilador. No padrão (/std:c++14) e /std:c++17 modos, o compilador emite aviso C5208 para código não conforme. Se /permissive- for especificado, o compilador emite o aviso C5208 como um erro em /std:c++14 e emite o erro C7626 em /std:c++17. O compilador emite o erro C7626 para código não conforme quando /std:c++20 ou /std:c++latest é especificado.

O exemplo a seguir mostra as construções que não são mais permitidas em estruturas sem nome. Dependendo do modo de padrões especificado, erros ou avisos C5208 ou C7626 são emitidos:

struct B { };
typedef struct : B { // inheriting from 'B'; ill-formed
    void f(); // ill-formed
    static int i; // ill-formed
    struct U {
        void f(); // nested class has non-data member; ill-formed
    };
    int j = 10; // default member initializer; ill-formed
} S;

O código acima pode ser corrigido dando um nome à classe sem nome:

struct B { };
typedef struct S_ : B {
    void f();
    static int i;
    struct U {
        void f();
    };
    int j = 10;
} S;

Importação de argumento padrão em C++/CLI

Um número crescente de APIs tem argumentos padrão no .NET Core. Portanto, agora oferecemos suporte à importação de argumentos padrão em C++/CLI. Essa alteração pode quebrar o código existente onde várias sobrecargas são declaradas, como neste exemplo:

public class R {
    public void Func(string s) {}   // overload 1
    public void Func(string s, string s2 = "") {} // overload 2;
}

Quando essa classe é importada para C++/CLI, uma chamada para uma das sobrecargas causa um erro:

    (gcnew R)->Func("abc"); // error C2668: 'R::Func' ambiguous call to overloaded function

O compilador emite o erro C2668 porque ambas as sobrecargas correspondem a esta lista de argumentos. Na segunda sobrecarga, o segundo argumento é preenchido pelo argumento padrão. Para contornar esse problema, você pode excluir a sobrecarga redundante (1). Ou, use a lista de argumentos completa e forneça explicitamente os argumentos padrão.

Melhorias de conformidade no Visual Studio 2019 versão 16.7

é uma definição trivialmente copiável

C++20 mudou a definição de é trivialmente copiável. Quando uma classe tem um membro de dados não estáticos com volatile tipo qualificado, isso não implica mais que qualquer cópia gerada pelo compilador ou construtor de movimento, ou operador de atribuição de cópia ou movimento, não é trivial. O comitê do C++ Standard aplicou essa alteração retroativamente como um Relatório de Defeitos. No MSVC, o comportamento do compilador não muda em diferentes modos de linguagem, como /std:c++14 ou /std:c++latest.

Aqui está um exemplo do novo comportamento:

#include <type_traits>

struct S
{
    volatile int m;
};

static_assert(std::is_trivially_copyable_v<S>, "Meow!");

Este código não é compilado em versões do MSVC anteriores ao Visual Studio 2019 versão 16.7. Há um aviso de compilador off-by-default que você pode usar para detetar essa alteração. Se você compilar o código acima usando cl /W4 /w45220o , verá o seguinte aviso:

warning C5220: `'S::m': a non-static data member with a volatile qualified type no longer implies that compiler generated copy/move constructors and copy/move assignment operators are non trivial`

As conversões literais de ponteiro para membro e cadeia de caracteres estão se bool estreitando

O comitê do C++ Standard adotou recentemente o Defect Report P1957R2, que considera T*bool uma conversão estreita. O MSVC corrigiu um bug em sua implementação, que anteriormente diagnosticava T* como bool estreitamento, mas não diagnosticava a conversão de uma string literal para bool ou de um ponteiro para membro para bool.

O seguinte programa está mal formado no Visual Studio 2019 versão 16.7:

struct X { bool b; };
void f(X);

int main() {
    f(X { "whoops?" }); // error: conversion from 'const char [8]' to 'bool' requires a narrowing conversion

    int (X::* p) = nullptr;
    f(X { p }); // error: conversion from 'int X::*' to 'bool' requires a narrowing conversion
}

Para corrigir esse código, adicione comparações explícitas ao nullptr, ou evite contextos em que as conversões de estreitamento são mal formadas:

struct X { bool b; };
void f(X);

int main() {
    f(X { "whoops?" != nullptr }); // Absurd, but OK

    int (X::* p) = nullptr;
    f(X { p != nullptr }); // OK
}

nullptr_t é apenas conversível em bool como uma inicialização direta

Em C++11, nullptr só é conversível em bool como uma conversão direta, por exemplo, quando você inicializa um bool usando uma lista de inicializadores preparada. Esta restrição nunca foi aplicada pela MSVC. O MSVC agora implementa a regra em /permissive-. As conversões implícitas são agora diagnosticadas como mal formadas. Uma conversão contextual para bool ainda é permitida, porque a inicialização bool b(nullptr) direta é válida.

Na maioria dos casos, o erro pode ser corrigido substituindo nullptr por false, como mostrado neste exemplo:

struct S { bool b; };
void g(bool);
bool h() { return nullptr; } // error, should be 'return false;'

int main() {
    bool b1 = nullptr; // error: cannot convert from 'nullptr' to 'bool'
    S s { nullptr }; // error: cannot convert from 'nullptr' to 'bool'
    g(nullptr); // error: cannot convert argument 1 from 'nullptr' to 'bool'

    bool b2 { nullptr }; // OK: Direct-initialization
    if (!nullptr) {} // OK: Contextual conversion to bool
}

Conformando o comportamento de inicialização para inicializações de matriz com inicializadores ausentes

Anteriormente, o MSVC tinha um comportamento não conforme para inicializações de matriz que tinham inicializadores ausentes. MSVC sempre chamou o construtor padrão para cada elemento de matriz que não tinha um inicializador. O comportamento padrão é inicializar cada elemento com uma lista{} de inicializadores () vazia. O contexto de inicialização para uma lista de inicializadores vazios é copy-initialization, que não permite chamadas para construtores explícitos. Também pode haver diferenças de tempo de execução, porque o uso de {} para inicializar pode chamar um construtor que usa um std::initializer_list, em vez do construtor padrão. O comportamento de conformidade é ativado em /permissive-.

Aqui está um exemplo do comportamento alterado:

struct B {
    explicit B() {}
};

void f() {
    B b1[1]{}; // Error in /permissive-, because aggregate init calls explicit ctor
    B b2[1]; // OK: calls default ctor for each array element
}

A inicialização de membros da classe com nomes sobrecarregados é sequenciada corretamente

Identificamos um bug na representação interna dos membros de dados de classe quando um nome de tipo também é sobrecarregado como o nome de um membro de dados. Esse bug causou inconsistências na inicialização agregada e na ordem de inicialização do membro. O código de inicialização gerado agora está correto. No entanto, essa alteração pode levar a erros ou avisos na fonte que inadvertidamente confiaram nos membros mal ordenados, como neste exemplo:

// Compiling with /w15038 now gives:
// warning C5038: data member 'Outer::Inner' will be initialized after data member 'Outer::v'
struct Outer {
    Outer(int i, int j) : Inner{ i }, v{ j } {}

    struct Inner { int x; };
    int v;
    Inner Inner; // 'Inner' is both a type name and data member name in the same scope
};

Em versões anteriores, o construtor inicializava incorretamente o membro Inner de dados antes do membro vde dados . (O padrão C++ requer uma ordem de inicialização que é a mesma que a ordem de declaração dos membros). Agora que o código gerado segue o padrão, o member-init-list está fora de ordem. O compilador gera um aviso para este exemplo. Para corrigi-lo, reordene a lista de membros-inicializadores para refletir a ordem da declaração.

Resolução de sobrecargas envolvendo sobrecargas integrais e long argumentos

O padrão C++ requer a classificação a long para int conversão como uma conversão padrão. Compiladores MSVC anteriores classificam-no incorretamente como uma promoção integral, que classifica mais alto para resolução de sobrecarga. Essa classificação pode fazer com que a resolução de sobrecarga seja resolvida com sucesso quando deve ser considerada ambígua.

O compilador agora considera a classificação corretamente no /permissive- modo. Código inválido é diagnosticado corretamente, como neste exemplo:

void f(long long);
void f(int);

int main() {
    long x {};
    f(x); // error: 'f': ambiguous call to overloaded function
    f(static_cast<int>(x)); // OK
}

Você pode corrigir esse problema de várias maneiras:

  • No site de chamada, altere o tipo do argumento passado para int. Você pode alterar o tipo de variável ou transmiti-la.

  • Se houver muitos sites de chamada, você pode adicionar outra sobrecarga que leva um long argumento. Nesta função, lance e encaminhe o argumento para a int sobrecarga.

Utilização de variável indefinida com ligação interna

As versões do MSVC anteriores à versão 16.7 do Visual Studio 2019 aceitavam o uso de uma variável declarada extern que tinha ligação interna e não estava definida. Tais variáveis não podem ser definidas em nenhuma outra unidade de tradução e não podem formar um programa válido. O compilador agora diagnostica este caso em tempo de compilação. O erro é semelhante ao erro para funções estáticas indefinidas.

namespace {
    extern int x; // Not a definition, but has internal linkage because of the anonymous namespace
}

int main()
{
    return x; // Use of 'x' that no other translation unit can possibly define.
}

Este programa anteriormente compilado incorretamente e vinculado, mas agora emitirá o erro C7631.

error C7631: 'anonymous-namespace::x': variable with internal linkage declared but not defined

Tais variáveis devem ser definidas na mesma unidade de tradução em que são usadas. Por exemplo, você pode fornecer um inicializador explícito ou uma definição separada.

Completude de tipo e conversões de ponteiro derivadas para base

Nos padrões C++ antes do C++20, uma conversão de uma classe derivada para uma classe base não exigia que a classe derivada fosse um tipo de classe completo. O comitê padrão C++ aprovou uma alteração retroativa do Relatório de Defeito que se aplica a todas as versões da linguagem C++. Essa alteração alinha o processo de conversão com características de tipo, como std::is_base_of, que exigem que a classe derivada seja um tipo de classe completo.

Aqui está um exemplo:

template<typename A, typename B>
struct check_derived_from
{
    static A a;
    static constexpr B* p = &a;
};

struct W { };
struct X { };
struct Y { };

// With this change this code will fail as Z1 is not a complete class type
struct Z1 : X, check_derived_from<Z1, X>
{
};

// This code failed before and it will still fail after this change
struct Z2 : check_derived_from<Z2, Y>, Y
{
};

// With this change this code will fail as Z3 is not a complete class type
struct Z3 : W
{
    check_derived_from<Z3, W> cdf;
};

Essa alteração de comportamento se aplica a todos os modos de linguagem C++ do MSVC, não apenas /std:c++20 ou /std:c++latest.

O estreitamento das conversões é diagnosticado de forma mais consistente

O MSVC emite um aviso para estreitar conversões em um inicializador de lista preparada. Anteriormente, o compilador não diagnosticava conversões de estreitamento de tipos subjacentes maiores enum para tipos integrais mais estreitos. (O compilador incorretamente considerou-os uma promoção integral em vez de uma conversão). Se a conversão de estreitamento for intencional, você poderá evitar o aviso usando um static_cast argumento no inicializador. Ou escolha um tipo de integral de destino maior.

Aqui está um exemplo de uso de um explícito static_cast para abordar o aviso:

enum E : long long { e1 };
struct S { int i; };

void f(E e) {
    S s = { e }; // warning: conversion from 'E' to 'int' requires a narrowing conversion
    S s1 = { static_cast<int>(e) }; // Suppress warning with explicit conversion
}

Melhorias de conformidade no Visual Studio 2019 versão 16.8

Extensão «Class rvalue used as lvalue»

MSVC tem uma extensão que permite usar uma classe rvalue como um lvalue. A extensão não estende o tempo de vida do rvalue de classe e pode levar a um comportamento indefinido em tempo de execução. Aplicamos agora a regra padrão e não permitimos esta extensão ao abrigo /permissive-da . Se você não pode usar /permissive- ainda, você pode usar /we4238 para explicitamente não permitir a extensão. Aqui está um exemplo:

// Compiling with /permissive- now gives:
// error C2102: '&' requires l-value
struct S {};

S f();

void g()
{
    auto p1 = &(f()); // The temporary returned by 'f' is destructed after this statement. So 'p1' points to an invalid object.

    const auto &r = f(); // This extends the lifetime of the temporary returned by 'f'
    auto p2 = &r; // 'p2' points to a valid object
}

Extensão 'Especialização explícita em escopo não namespace'

O MSVC tinha uma extensão que permitia especialização explícita em escopo que não fosse namespace. Agora faz parte do padrão, após a resolução do CWG 727. No entanto, existem diferenças de comportamento. Ajustamos o comportamento do nosso compilador para nos alinharmos com o padrão.

// Compiling with 'cl a.cpp b.cpp /permissive-' now gives:
//   error LNK2005: "public: void __thiscall S::f<int>(int)" (??$f@H@S@@QAEXH@Z) already defined in a.obj
// To fix the linker error,
// 1. Mark the explicit specialization with 'inline' explicitly. Or,
// 2. Move its definition to a source file.

// common.h
struct S {
    template<typename T> void f(T);
    template<> void f(int);
};

// This explicit specialization is implicitly inline in the default mode.
template<> void S::f(int) {}

// a.cpp
#include "common.h"

int main() {}

// b.cpp
#include "common.h"

Verificação de tipos de classe abstratos

O C++20 Standard alterou o processo que os compiladores usam para detetar o uso de um tipo de classe abstrata como um parâmetro de função. Especificamente, não é mais um erro SFINAE. Anteriormente, se o compilador detetasse que uma especialização de um modelo de função teria uma instância de tipo de classe abstrata como um parâmetro de função, então essa especialização seria considerada mal formada. Não seria adicionado ao conjunto de funções candidatas viáveis. Em C++20, a verificação de um parâmetro de tipo de classe abstrata não acontece até que a função seja chamada. O efeito é que o código usado para compilar não causará um erro. Aqui está um exemplo:

class Node {
public:
    int index() const;
};

class String : public Node {
public:
    virtual int size() const = 0;
};

class Identifier : public Node {
public:
    const String& string() const;
};

template<typename T>
int compare(T x, T y)
{
    return x < y ? -1 : (x > y ? 1 : 0);
}

int compare(const Node& x, const Node& y)
{
    return compare(x.index(), y.index());
}

int f(const Identifier& x, const String& y)
{
    return compare(x.string(), y);
}

Anteriormente, a chamada para compare teria tentado especializar o modelo compare de função usando um String argumento de modelo para T. Não conseguiria gerar uma especialização válida, porque String é uma classe abstrata. O único candidato viável teria sido compare(const Node&, const Node&). No entanto, em C++20, a verificação do tipo de classe abstrata não acontece até que a função seja chamada. Assim, a especialização compare(String, String) é adicionada ao conjunto de candidatos viáveis, e é escolhida como a melhor candidata porque a conversão de para é uma sequência de conversão melhor const String& do que a conversão de String para const String&const Node& .

Em C++20, uma correção possível para este exemplo é usar conceitos; ou seja, alterar a definição de compare :

template<typename T>
int compare(T x, T y) requires !std::is_abstract_v<T>
{
    return x < y ? -1 : (x > y ? 1 : 0);
}

Ou, se os conceitos de C++ não estiverem disponíveis, você pode recorrer ao SFINAE:

template<typename T, std::enable_if_t<!std::is_abstract_v<T>, int> = 0>
int compare(T x, T y)
{
    return x < y ? -1 : (x > y ? 1 : 0);
}

Suporte para P0960R3 - permitir a inicialização de agregações a partir de uma lista de valores entre parênteses

O C++20 P0960R3 adiciona suporte para inicializar uma agregação usando uma lista de inicializadores entre parênteses. Por exemplo, o código a seguir é válido em C++20:

struct S {
    int i;
    int j;
};

S s(1, 2);

A maior parte desse recurso é aditiva, ou seja, o código agora compila que não compilava antes. No entanto, isso muda o comportamento do std::is_constructible. No modo C++17 isso static_assert falha, mas no modo C++20 é bem-sucedido:

static_assert(std::is_constructible_v<S, int, int>, "Assertion failed!");

Se você usar essa característica-tipo para controlar a resolução de sobrecarga, isso pode levar a uma alteração no comportamento entre C++17 e C++20.

Resolução de sobrecarga envolvendo modelos de função

Anteriormente, o compilador permitia que algum código fosse compilado sob /permissive- o que não deveria ser compilado. O efeito foi, o compilador chamou a função errada levando a uma mudança no comportamento de tempo de execução:

int f(int);

namespace N
{
    using ::f;
    template<typename T>
    T f(T);
}

template<typename T>
void g(T&& t)
{
}

void h()
{
    using namespace N;
    g(f);
}

A chamada para g usa um conjunto de sobrecarga que contém duas funções ::f e N::f. Como N::f é um modelo de função, o compilador deve tratar o argumento da função como um contexto não deduzido. Isso significa que, neste caso, a chamada para g deve falhar, pois o compilador não pode deduzir um tipo para o parâmetro Ttemplate . Infelizmente, o compilador não descartou o fato de que já havia decidido que ::f era uma boa combinação para a chamada de função. Em vez de emitir um erro, o compilador geraria código para chamar g usando ::f como argumento.

Dado que, em muitos casos, usar ::f como o argumento da função é o que o usuário espera, só emitimos um erro se o código for compilado com /permissive-.

Migrando de co-rotinas para /await C++20

As co-rotinas padrão do C++20 agora estão ativadas por padrão em /std:c++20 e /std:c++latest. Eles diferem das Co-rotinas TS e do suporte sob a /await opção. A migração de co-rotinas para co-rotinas padrão pode exigir algumas alterações de /await origem.

Palavras-chave não padronizadas

O antigo await e yield as palavras-chave não são suportados no modo C++20. Código deve usar co_await e co_yield em vez disso. O modo padrão também não permite o uso de return em uma co-rotina. Cada return um em uma co-rotina deve usar co_return.

// /await
task f_legacy() {
    ...
    await g();
    return n;
}
// /std:c++latest
task f() {
    ...
    co_await g();
    co_return n;
}

Tipos de initial_suspend/final_suspend

Em /await, as funções de promessa inicial e suspensão podem ser declaradas como retornando bool. Esse comportamento não é padrão. Em C++20, essas funções devem retornar um tipo de classe aguardado, geralmente um dos tipos triviais esperados: std::suspend_always se a função retornou trueanteriormente ou std::suspend_never se retornou false.

// /await
struct promise_type_legacy {
    bool initial_suspend() noexcept { return false; }
    bool final_suspend() noexcept { return true; }
    ...
};

// /std:c++latest
struct promise_type {
    auto initial_suspend() noexcept { return std::suspend_never{}; }
    auto final_suspend() noexcept { return std::suspend_always{}; }
    ...
};

Tipo de seringa yield_value

Em C++20, a função promise yield_value deve retornar um tipo aguardado. No /await modo, a yield_value função tinha permissão para retornar voide sempre suspendia. Tais funções podem ser substituídas por uma função que retorna std::suspend_always.

// /await
struct promise_type_legacy {
    ...
    void yield_value(int x) { next = x; };
};

// /std:c++latest
struct promise_type {
    ...
    auto yield_value(int x) { next = x; return std::suspend_always{}; }
};

Função de tratamento de exceções

/await suporta um tipo de promessa sem função de tratamento de exceções ou uma função de tratamento de exceções denominada set_exception que usa um std::exception_ptrarquivo . Em C++20, o tipo de promessa deve ter uma função chamada unhandled_exception que não usa argumentos. O objeto de exceção pode ser obtido de std::current_exception se necessário.

// /await
struct promise_type_legacy {
    void set_exception(std::exception_ptr e) { saved_exception = e; }
    ...
};
// /std:c++latest
struct promise_type {
    void unhandled_exception() { saved_exception = std::current_exception(); }
    ...
};

Tipos de retorno deduzido de co-rotinas não suportadas

O C++20 não oferece suporte a co-rotinas com um tipo de retorno que inclui um tipo de espaço reservado, como auto. Os tipos de retorno de co-rotinas devem ser explicitamente declarados. Em /await, esses tipos deduzidos sempre envolvem um tipo experimental e exigem a inclusão de um cabeçalho que define o tipo necessário: Um de std::experimental::task<T>, std::experimental::generator<T>ou std::experimental::async_stream<T>.

// /await
auto my_generator() {
    ...
    co_yield next;
}

// /std:c++latest
#include <experimental/generator>
std::experimental::generator<int> my_generator() {
    ...
    co_yield next;
}

Tipo de devolução de return_value

O tipo de retorno da função promise return_value deve ser void. No /await modo, o tipo de retorno pode ser qualquer coisa e é ignorado. Esse diagnóstico pode ajudar a detetar erros sutis, como quando o autor assume incorretamente que o valor de retorno de return_value é retornado a um chamador.

// /await
struct promise_type_legacy {
    ...
    int return_value(int x) { return x; } // incorrect, the return value of this function is unused and the value is lost.
};

// /std:c++latest
struct promise_type {
    ...
    void return_value(int x) { value = x; }; // save return value
};

Comportamento de conversão de objeto de retorno

Se o tipo de retorno declarado de uma co-rotina não corresponder ao tipo de retorno da função promise get_return_object , o objeto retornado de get_return_object será convertido para o tipo de retorno da co-rotina. Sob /await, essa conversão é feita cedo, antes que o corpo de co-rotina tenha a chance de executar. Em /std:c++20 ou /std:c++latest, essa conversão é feita quando o valor é retornado ao chamador. Ele permite que co-rotinas que não suspendem no ponto de suspensão inicial façam uso do objeto retornado por get_return_object dentro do corpo da co-rotina.

Parâmetros de promessa de co-rotina

Em C++20, o compilador tenta passar os parâmetros de co-rotina (se houver) para um construtor do tipo promise. Se falhar, ele tenta novamente com um construtor padrão. No /await modo, apenas o construtor padrão foi usado. Essa mudança pode levar a uma diferença de comportamento se a promessa tiver vários construtores. Ou, se houver uma conversão de um parâmetro de co-rotina para o tipo de promessa.

struct coro {
    struct promise_type {
        promise_type() { ... }
        promise_type(int x) { ... }
        ...
    };
};

coro f1(int x);

// Under /await the promise gets constructed using the default constructor.
// Under /std:c++latest the promise gets constructed using the 1-argument constructor.
f1(0);

struct Object {
template <typename T> operator T() { ... } // Converts to anything!
};

coro f2(Object o);

// Under /await the promise gets constructed using the default constructor
// Under /std:c++latest the promise gets copy- or move-constructed from the result of
// Object::operator coro::promise_type().
f2(Object{});

/permissive- e os módulos C++20 estão ativados por padrão em /std:c++20

O suporte aos módulos C++20 está ativado por padrão em /std:c++20 e /std:c++latest. Para obter mais informações sobre essa alteração e os cenários em que module e import são tratados condicionalmente como palavras-chave, consulte Suporte a módulos C++20 padrão com MSVC no Visual Studio 2019 versão 16.8.

Como pré-requisito para o suporte a módulos, permissive- agora está habilitado quando /std:c++20 ou /std:c++latest é especificado. Para obter mais informações, consulte /permissive-.

Para código que compilado anteriormente sob /std:c++latest e requer comportamentos de compilador não conformes, /permissive pode ser especificado para desativar o modo de conformidade estrita no compilador. A opção do compilador deve aparecer depois /std:c++latest na lista de argumentos da linha de comando. No entanto, /permissive resulta em um erro se o uso de módulos for detetado:

erro C1214: Módulos em conflito com comportamento não-padrão solicitado via 'option'

Os valores mais comuns para a opção são:

Opção Descrição
/Zc:twoPhase- A pesquisa de nome bifásica é necessária para módulos C++20 e implícita no /permissive-.
/Zc:hiddenFriend- As regras padrão de pesquisa de nome de amigo oculto são necessárias para os módulos C++20 e implícitas no /permissive-.
/Zc:lambda- O processamento lambda padrão é necessário para módulos C++20 e está implícito por /std:c++20 modo ou posterior.
/Zc:preprocessor- O pré-processador em conformidade é necessário apenas para uso e criação da unidade de cabeçalho C++20. Os módulos nomeados não exigem essa opção.

A /experimental:module opção ainda é necessária para usar os módulos fornecidos com o std.* Visual Studio, porque eles ainda não são padronizados.

A /experimental:module opção também implica /Zc:twoPhase, /Zc:lambdae /Zc:hiddenFriend. Anteriormente, o código compilado com módulos às vezes podia ser compilado com /Zc:twoPhase- se o módulo fosse apenas consumido. Esse comportamento não é mais suportado.

Melhorias de conformidade no Visual Studio 2019 versão 16.9

Inicialização de cópia de inicialização direta temporária em referência

A questão CWG 2267 do Core Working Group lidou com uma inconsistência entre uma lista de inicializadores entre parênteses e uma lista de inicializadores preparada. A resolução harmoniza as duas formas.

O Visual Studio 2019 versão 16.9 implementa o comportamento alterado em todos os /std modos de compilador. No entanto, como é potencialmente uma alteração de quebra de fonte, ela só é suportada se o código for compilado usando /permissive-.

Este exemplo demonstra a mudança no comportamento:

struct A { };

struct B {
    explicit B(const A&);
};

void f()
{
    A a;
    const B& b1(a);     // Always an error
    const B& b2{ a };   // Allowed before resolution to CWG 2267 was adopted: now an error
}

Características do destruidor e subobjetos potencialmente construídos

A edição CWG 2336 do Core Working Group cobre uma omissão sobre especificações de exceção implícitas de destruidores em classes que têm classes base virtuais. A omissão significava que um destruidor em uma classe derivada poderia ter uma especificação de exceção mais fraca do que uma classe base, se essa base fosse abstrata e tivesse uma virtual base.

O Visual Studio 2019 versão 16.9 implementa o comportamento alterado em todos os /std modos de compilador.

Este exemplo mostra como a interpretação mudou:

class V {
public:
    virtual ~V() noexcept(false);
};

class B : virtual V {
    virtual void foo () = 0;
    // BEFORE: implicitly defined virtual ~B() noexcept(true);
    // AFTER: implicitly defined virtual ~B() noexcept(false);
};

class D : B {
    virtual void foo ();
    // implicitly defined virtual ~D () noexcept(false);
};

Antes desta mudança, o destruidor implicitamente definido para B era noexcept, porque apenas subobjetos potencialmente construídos são considerados. E a classe V base não é um subobjeto potencialmente construído, porque é uma virtual base e B é abstrata. No entanto, a classe V base é um subobjeto potencialmente construído da classe D, e assim D::~D é determinado como sendo noexcept(false), levando a uma classe derivada com uma especificação de exceção mais fraca do que sua base. Esta interpretação não é segura. Isso pode levar a um comportamento de tempo de execução incorreto se uma exceção for lançada de um destruidor de uma classe derivada de B.

Com essa alteração, um destruidor também está potencialmente lançando se tiver um destruidor virtual e qualquer classe base virtual tiver um destruidor potencialmente lançando.

Tipos semelhantes e vinculação de referência

A edição do Grupo de Trabalho Principal CWG 2352 trata de uma inconsistência entre as regras vinculativas de referência e as alterações à semelhança de tipos. A inconsistência foi introduzida em relatórios de defeitos anteriores (como o CWG 330). Isso afetou as versões 16.0 a 16.8 do Visual Studio 2019.

Com essa alteração, a partir do Visual Studio 2019 versão 16.9, o código que anteriormente vinculava uma referência a um temporário no Visual Studio 2019 versão 16.0 a 16.8 agora pode vincular diretamente quando os tipos envolvidos diferem apenas por qualificadores cv.

O Visual Studio 2019 versão 16.9 implementa o comportamento alterado em todos os /std modos de compilador. É potencialmente uma mudança de quebra de fonte.

Consulte Referências a tipos com qualificadores de cv incompatíveis para uma alteração relacionada.

Este exemplo mostra o comportamento alterado:

int *ptr;
const int *const &f() {
    return ptr; // Now returns a reference to 'ptr' directly.
    // Previously returned a reference to a temporary and emitted C4172
}

A atualização pode alterar o comportamento do programa que dependia de um temporário introduzido:

int func() {
    int i1 = 13;
    int i2 = 23;
    
    int* iptr = &i1;
    int const * const&  iptrcref = iptr;

    // iptrcref is a reference to a pointer to i1 with value 13.
    if (*iptrcref != 13)
    {
        return 1;
    }
    
    // Now change what iptr points to.

    // Prior to CWG 2352 iptrcref should be bound to a temporary and still points to the value 13.
    // After CWG 2352 it is bound directly to iptr and now points to the value 23.
    iptr = &i2;
    if (*iptrcref != 23)
    {
        return 1;
    }

    return 0;
}

/Zc:twoPhase e /Zc:twoPhase- mudança de comportamento da opção

Normalmente, as opções do compilador MSVC funcionam com o princípio de que o último visto vence. Infelizmente, não foi o caso com as /Zc:twoPhase e /Zc:twoPhase- opções. Essas opções eram "pegajosas", então as opções posteriores não podiam substituí-las. Por exemplo:

cl /Zc:twoPhase /permissive a.cpp

Nesse caso, a primeira /Zc:twoPhase opção permite uma pesquisa rigorosa de nomes em duas fases. A segunda opção destina-se a desativar o modo de conformidade estrita (é o oposto de /permissive-), mas não desativou /Zc:twoPhase.

O Visual Studio 2019 versão 16.9 altera esse comportamento em todos os /std modos de compilador. /Zc:twoPhase e /Zc:twoPhase- não são mais "pegajosos", e opções posteriores podem substituí-los.

Especificadores de noexcetuo explícitos em modelos de destruidores

O compilador aceitou anteriormente um modelo de destruidor declarado com uma especificação de exceção não lançada, mas definido sem um especificador noexcept-explícito. A especificação de exceção implícita de um destruidor depende das propriedades da classe - propriedades que podem não ser conhecidas no ponto de definição de um modelo. O padrão C++ também requer esse comportamento: Se um destruidor é declarado sem um especificador noexcept, então ele tem uma especificação de exceção implícita, e nenhuma outra declaração da função pode ter um especificador noexcept.

O Visual Studio 2019 versão 16.9 muda para o comportamento de conformidade em todos os /std modos de compilador.

Este exemplo mostra a mudança no comportamento do compilador:

template <typename T>
class B {
    virtual ~B() noexcept; // or throw()
};

template <typename T>
B<T>::~B() { /* ... */ } // Before: no diagnostic.
// Now diagnoses a definition mismatch. To fix, define the implementation by 
// using the same noexcept-specifier. For example,
// B<T>::~B() noexcept { /* ... */ }

Expressões reescritas em C++20

Desde o Visual Studio 2019 versão 16.2, em /std:c++latest, o compilador aceitou código como este exemplo:

#include <compare>

struct S {
    auto operator<=>(const S&) const = default;
    operator bool() const;
};

bool f(S a, S b) {
    return a < b;
}

No entanto, o compilador não invocaria a função de comparação que o autor poderia esperar. O código acima deve ter sido reescrito a < b como (a <=> b) < 0. Em vez disso, o compilador usou a função de conversão definida pelo operator bool() usuário e comparou bool(a) < bool(b). No Visual Studio 2019 versão 16.9 e posterior, o compilador reescreve a expressão usando a expressão de operador de nave espacial esperada.

Alteração de rutura na fonte

Aplicar corretamente conversões a expressões reescritas tem outro efeito: o compilador também diagnostica corretamente ambiguidades de tentativas de reescrever a expressão. Considere este exemplo:

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

struct Derived : Base {
    Derived();
    Derived(const Base&);
    bool operator==(const Derived& rhs) const;
};

bool b = Base{} == Derived{};

Em C++17, esse código seria aceito devido à conversão derivada para base de Derived no lado direito da expressão. Em C++20, a expressão sintetizada candidata também é adicionada: Derived{} == Base{}. Por causa das regras no padrão sobre qual função ganha com base em conversões, acontece que a escolha entre Base::operator== e Derived::operator== é indecidível. Como as sequências de conversão nas duas expressões não são melhores ou piores uma do que a outra, o código de exemplo resulta em uma ambiguidade.

Para resolver a ambiguidade, adicione um novo candidato que não estará sujeito às duas sequências de conversão:

bool operator==(const Derived&, const Base&);

Alteração de quebra de tempo de execução

Devido às regras de reescrita do operador em C++20, é possível que a resolução de sobrecarga encontre um novo candidato que, de outra forma, não encontraria em um modo de idioma inferior. E o novo candidato pode ser melhor do que o candidato mais antigo. Considere este exemplo:

struct iterator;
struct const_iterator {
  const_iterator(const iterator&);
  bool operator==(const const_iterator &ci) const;
};

struct iterator {
  bool operator==(const const_iterator &ci) const { return ci == *this; }
};

Em C++17, o único candidato para ci == *this é const_iterator::operator==. É uma partida porque *this passa por uma conversão derivada para base para const_iterator. Em C++20, outro candidato reescrito é adicionado: *this == ci, que invoca iterator::operator==. Este candidato não requer conversões, por isso é uma combinação melhor do que const_iterator::operator==. O problema com o novo candidato é que é a função atualmente sendo definida, então a nova semântica da função causa uma definição infinitamente recursiva de iterator::operator==.

Para ajudar no código como o exemplo, o compilador implementa um novo aviso:

$ cl /std:c++latest /c t.cpp
t.cpp
t.cpp(8): warning C5232: in C++20 this comparison calls 'bool iterator::operator ==(const const_iterator &) const' recursively

Para corrigir o código, seja explícito sobre qual conversão usar:

struct iterator {
  bool operator==(const const_iterator &ci) const { return ci == static_cast<const const_iterator&>(*this); }
};

Melhorias de conformidade no Visual Studio 2019 versão 16.10

Sobrecarga errada escolhida para inicialização de cópia da classe

Dado este código de exemplo:

struct A { template <typename T> A(T&&); };
struct B { operator A(); };
struct C : public B{};
void f(A);
f(C{});

Versões anteriores do compilador converteriam incorretamente o argumento de de type para an f usando o construtor de conversão de C modelo de A.A Em vez disso, o C++ padrão requer o uso do operador B::operator A de conversão. No Visual Studio 2019 versão 16.10 e posterior, o comportamento de resolução de sobrecarga é alterado para usar a sobrecarga correta.

Esta alteração também pode corrigir a sobrecarga escolhida em algumas outras situações:

struct Base 
{
    operator char *();
};

struct Derived : public Base
{
    operator bool() const;
};

void f(Derived &d)
{
    // Implicit conversion to bool previously used Derived::operator bool(), now uses Base::operator char*.
    // The Base function is preferred because operator bool() is declared 'const' and requires a qualification
    // adjustment for the implicit object parameter, while the Base function does not.
    if (d)
    {
        // ...
    }
}

Análise incorreta de literais de vírgula flutuante

No Visual Studio 2019 versão 16.10 e posterior, literais de ponto flutuante são analisados com base em seu tipo real. As versões anteriores do compilador sempre analisavam um literal de ponto flutuante como se tivesse tipo double e, em seguida, convertiam o resultado para o tipo real. Esse comportamento pode levar a arredondamento incorreto e rejeição de valores válidos:

// The binary representation is '0x15AE43FE' in VS2019 16.9
// The binary representation is '0x15AE43FD' in VS2019 16.10
// You can use 'static_cast<float>(7.038531E-26)' if you want the old behavior.
float f = 7.038531E-26f;

Ponto de declaração incorreto

Versões anteriores do compilador não podiam compilar código autorreferencial como este exemplo:

struct S {
    S(int, const S*);

    int value() const;
};

S s(4, &s);

O compilador não declararia a variável s até analisar toda a declaração, incluindo os argumentos do construtor. A pesquisa do na lista de s argumentos do construtor falharia. No Visual Studio 2019 versão 16.10 e posterior, este exemplo agora compila corretamente.

Infelizmente, essa alteração pode quebrar o código existente, como neste exemplo:

S s(1, nullptr); // outer s
// ...
{
   S s(s.value(), nullptr); // inner s
}

Em versões anteriores do compilador, quando ele procura s nos argumentos do construtor para a declaração "inner" de s, ele encontra a declaração anterior ("outer" s) e o código compila. A partir da versão 16.10, o compilador emite o aviso C4700 . É porque o compilador agora declara o "interno" s antes de analisar os argumentos do construtor. Assim, a s pesquisa encontra o "interno", sque ainda não foi inicializado.

Membro explicitamente especializado de um modelo de classe

Versões anteriores do compilador marcaram incorretamente uma especialização explícita de um membro do modelo de classe como inline se ele também fosse definido no modelo primário. Esse comportamento significava que o compilador às vezes rejeitava o código conforme. No Visual Studio 2019 versão 16.10 e posterior, uma especialização explícita não é mais marcada implicitamente como inline no /permissive- modo. Considere este exemplo:

Ficheiro de origem s.h

// s.h
template<typename T>
struct S {
    int f() { return 1; }
};
template<> int S<int>::f() { return 2; }

Ficheiro de origem s.cpp

// s.cpp
#include "s.h"

Ficheiro de origem main.cpp

// main.cpp
#include "s.h"

int main()
{
}

Para resolver o erro do vinculador no exemplo acima, adicione inline explicitamente a S<int>::f:

template<> inline int S<int>::f() { return 2; }

Erro deduzido do nome do tipo de retorno

No Visual Studio 2019 versão 16.10 e posterior, o compilador alterou a forma como gera nomes distorcidos para funções que deduziram tipos de retorno. Por exemplo, considere estas funções:

auto f() { return 0; }
auto g() { []{}; return 0; }

Versões anteriores do compilador gerariam estes nomes para o vinculador:

f: ?f@@YAHXZ -> int __cdecl f(void)
g: ?g@@YA@XZ -> __cdecl g(void)

Surpreendentemente, o tipo de retorno seria omitido devido g a outro comportamento semântico causado pela lambda local no corpo da função. Essa inconsistência dificultou a implementação de funções exportadas que têm um tipo de retorno deduzido: A interface do módulo requer informações sobre como o corpo de uma função foi compilado. Ele precisa das informações para produzir uma função no lado da importação que possa ser adequadamente vinculada à definição.

O compilador agora omite o tipo de retorno de uma função de tipo de retorno deduzido. Esse comportamento é consistente com outras implementações principais. Há uma exceção para modelos de função: esta versão do compilador introduz um novo comportamento de nome emaranhado para modelos de função que têm um tipo de retorno deduzido:

template <typename T>
auto f(T) { return 1; }

template <typename T>
decltype(auto) g(T) { return 1.; }

int (*fp1)(int) = &f;
double (*fp2)(int) = &g;

Os nomes distorcidos para auto e decltype(auto) agora aparecem no binário, não no tipo de retorno deduzido:

f: ??$f@H@@YA?A_PH@Z -> auto __cdecl f<int>(int)
g: ??$g@H@@YA?A_TH@Z -> decltype(auto) __cdecl g<int>(int)

Versões anteriores do compilador incluiriam o tipo de retorno deduzido como parte da assinatura. Quando o compilador incluía o tipo de retorno no nome distorcido, isso poderia causar problemas com o vinculador. Alguns cenários bem formados tornar-se-iam ambíguos para o linker.

O novo comportamento do compilador pode produzir uma alteração de quebra binária. Considere este exemplo:

Ficheiro de origem a.cpp

// a.cpp
auto f() { return 1; }

Ficheiro de origem main.cpp

// main.cpp
int f();
int main() { f(); }

Em versões anteriores à versão 16.10, o compilador produziu um nome para auto f() que se parecesse int f()com , mesmo que fossem funções semanticamente distintas. Isso significa que o exemplo seria compilado. Para corrigir o problema, não confie auto na definição original de f. Em vez disso, escreva-o como int f(). Como as funções que deduziram tipos de retorno são sempre compiladas, as implicações do ABI são minimizadas.

Aviso para atributo ignorado nodiscard

As versões anteriores do compilador ignoravam silenciosamente certos usos de um nodiscard atributo. Eles ignoravam o atributo se ele estivesse em uma posição sintática que não se aplicasse à função ou classe que estava sendo declarada. Por exemplo:

static [[nodiscard]] int f() { return 1; }

No Visual Studio 2019 versão 16.10 e posterior, o compilador emite o aviso de nível 4 C5240:

a.cpp(1): warning C5240: 'nodiscard': attribute is ignored in this syntactic position

Para corrigir esse problema, mova o atributo para a posição sintática correta:

[[nodiscard]] static int f() { return 1; }

Aviso para include diretivas com nomes de cabeçalho do sistema na alçada do módulo

No Visual Studio 2019 versão 16.10 e posterior, o compilador emite um aviso para evitar um erro comum de criação da interface do módulo. Se você incluir um cabeçalho de biblioteca padrão após uma export module instrução, o compilador emitirá o aviso C5244. Aqui está um exemplo:

export module m;
#include <vector>

export
void f(std::vector<int>);

O desenvolvedor provavelmente não pretendia que o módulo m possuísse o conteúdo do <vector>. O compilador agora emite um aviso para ajudar a encontrar e corrigir o problema:

m.ixx(2): warning C5244: '#include <vector>' in the purview of module 'm' appears erroneous. Consider moving that directive before the module declaration, or replace the textual inclusion with an "import <vector>;".
m.ixx(1): note: see module 'm' declaration

Para corrigir esse problema, mova #include <vector> antes de export module m;:

#include <vector>
export module m;

export
void f(std::vector<int>);

Aviso para funções de ligação interna não utilizadas

No Visual Studio 2019 versão 16.10 e posterior, o compilador avisa em mais situações em que uma função não referenciada com ligação interna foi removida. Versões anteriores do compilador emitiriam aviso C4505 para o seguinte código:

static void f() // warning C4505: 'f': unreferenced function with internal linkage has been removed
{
}

O compilador agora também avisa sobre funções não referenciadas auto e funções não referenciadas em namespaces anônimos. Ele emite um aviso desativado por padrão C5245 para ambas as seguintes funções:

namespace
{
    void f1() // warning C5245: '`anonymous-namespace'::f1': unreferenced function with internal linkage has been removed
    {
    }
}

auto f2() // warning C5245: 'f2': unreferenced function with internal linkage has been removed
{
    return []{ return 13; };
}

Advertência sobre a elisão da cinta

No Visual Studio 2019 versão 16.10 e posterior, o compilador avisa sobre listas de inicialização que não usam chaves para subobjetos. O compilador emite o aviso C5246 off-by-default .

Aqui está um exemplo:

struct S1 {
  int i, j;
};

struct S2 {
   S1 s1;
   int k;
};

S2 s2{ 1, 2, 3 }; // warning C5246: 'S2::s1': the initialization of a subobject should be wrapped in braces

Para corrigir esse problema, envolva a inicialização do subobjeto em chaves:

S2 s2{ { 1, 2 }, 3 };

Detetar corretamente se um const objeto não foi inicializado

No Visual Studio 2019 versão 16.10 e posterior, o compilador agora emite o erro C2737 quando você tenta definir um const objeto que não está totalmente inicializado:

struct S {
   int i;
   int j = 2;
};

const S s; // error C2737: 's': const object must be initialized

Versões anteriores do compilador permitiam que esse código fosse compilado, mesmo que S::i não tenha sido inicializado.

Para corrigir esse problema, inicialize todos os membros antes de criar uma const instância de um objeto:

struct S {
   int i = 1;
   int j = 2;
};

Melhorias de conformidade no Visual Studio 2019 versão 16.11

/std:c++20 modo de compilador

No Visual Studio 2019 versão 16.11 e posterior, o compilador agora oferece suporte ao /std:c++20 modo de compilador. Anteriormente, os recursos do C++20 estavam disponíveis apenas no /std:c++latest modo no Visual Studio 2019. Os recursos do C++20 que originalmente exigiam /std:c++latest o modo agora funcionam no /std:c++20 modo ou posterior nas versões mais recentes do Visual Studio.

Ver também

Conformidade da Linguagem Microsoft C/C++