Visual Studio 2019'da C++ Uyumluluk geliştirmeleri, davranış değişiklikleri ve hata düzeltmeleri

Visual Studio'da (MSVC) Microsoft C/C++ her sürümde uyumluluk iyileştirmeleri ve hata düzeltmeleri yapar. Bu makalede, ana sürüme ve ardından sürüme göre iyileştirmeler listelenir. Belirli bir sürüme yönelik değişikliklere doğrudan atlamak için bu makalede aşağıdaki listeyi kullanın.

Bu belgede Visual Studio 2019'daki değişiklikler listelenir. Visual Studio 2022'deki değişikliklere yönelik bir kılavuz için bkz . Visual Studio 2022'de C++ uyumluluk geliştirmeleri. Visual Studio 2017'deki değişiklikler için bkz . Visual Studio 2017'de C++ uyumluluğu geliştirmeleri. Önceki uyumluluk iyileştirmelerinin tam listesi için bkz . Visual C++ Yenilikler 2003 ile 2015 arasında.

Visual Studio 2019 RTW'de uyumluluk geliştirmeleri (sürüm 16.0)

Visual Studio 2019 RTW, Microsoft C++ derleyicisinde aşağıdaki uyumluluk iyileştirmelerini, hata düzeltmelerini ve davranış değişikliklerini içerir.

Not

C++20 özellikleri, C++20 uygulamasının tamamlandığı kabul edilene kadar yalnızca /std:c++latest Visual Studio 2019 modunda kullanılabilir. Visual Studio 2019 sürüm 16.11 derleyici modunu tanıtır /std:c++20 . Bu makalede, başlangıçta gerekli /std:c++latest olan mod özellikleri artık Visual Studio'nun /std:c++20 en son sürümlerinde modda veya daha sonraki sürümlerde çalışır. Özellikler ilk kez kullanıma sunulduğunda bu seçenek kullanılamasa bile belgelerini bahsedecek /std:c++20şekilde güncelleştirdik.

Şablonlar ve hata algılama için geliştirilmiş modül desteği

Modüller artık resmi olarak C++20 standardındadır. Visual Studio 2017 sürüm 15.9'a geliştirilmiş destek eklendi. Daha fazla bilgi için bkz . MSVC 2017 sürüm 15.9 ile C++ Modüllerinde daha iyi şablon desteği ve hata algılama.

Toplama türünün değiştirilmiş belirtimi

C++20'de toplama türünün belirtimi değişti (bkz . Kullanıcı tarafından bildirilen oluşturucularla toplamaları yasaklama). Visual Studio 2019'da, (veya /std:c++20 Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) altında /std:c++latest , kullanıcı tarafından bildirilen herhangi bir oluşturucuya sahip bir sınıf (örneğin, bildirilen = default veya = delete) bir toplama değildir. Daha önce, yalnızca kullanıcı tarafından sağlanan oluşturucular bir sınıfın bir toplama olmasına izin vermeyacaktı. Bu değişiklik, bu tür türlerin nasıl başlatılacağı konusunda daha fazla kısıtlama getirir.

Aşağıdaki kod Visual Studio 2017'de hatasız derlenmiş ancak veya /std:c++latestaltında /std:c++20 Visual Studio 2019'da C2280 ve C2440 hatalarına neden olur:

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

Için kısmi destek operator <=>

P0515R3 C++20, "uzay gemisi operatörü" olarak da bilinen üç yönlü karşılaştırma işlecini tanıtır<=>. Modda Visual Studio 2019 sürüm 16.0 /std:c++latest , artık izin verilmeyen söz dizimi için hatalar çıkararak işleç için kısmi destek sağlar. Örneğin, aşağıdaki kod Visual Studio 2017'de hata olmadan derlenmiş ancak veya /std:c++latestaltında /std:c++20 Visual Studio 2019'da birden çok hata oluşturur:

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

Hataları önlemek için, son açılı ayraçtan önce sorunlu satıra bir boşluk ekleyin: U<&S::operator<= > u;.

Eşleşmeyen cv niteleyicileri olan türlere başvurular

Not

Bu değişiklik yalnızca Visual Studio 2019'un 16.0 ile 16.8 sürümlerini etkiler. Visual Studio 2019 sürüm 16.9'dan başlayarak geri döndürüldü

Daha önce, MSVC en üst düzeyin altında eşleşmeyen cv niteleyicileri olan bir türden bir başvurunun doğrudan bağlanmasına izin verdi. Bu bağlama, başvuru tarafından başvuruda bulunılan sözde sabit verilerin değiştirilmesine izin verebilir.

Visual Studio 2019'un 16.0 ile 16.8 sürümlerinin derleyicisi bunun yerine, o sırada standart için gerekli olduğu gibi geçici bir oluşturur. Daha sonra standart geriye dönük olarak değiştirilerek Visual Studio 2017 ve önceki sürümlerin davranışı doğru, Visual Studio 2019 sürüm 16.0 ile 16.8 arasında davranış yanlıştır. Sonuç olarak, bu değişiklik Visual Studio 2019 sürüm 16.9'dan başlayarak geri döndürüldü.

İlgili bir değişiklik için bkz . Benzer türler ve başvuru bağlaması .

Örneğin, Visual Studio 2017'de aşağıdaki kod uyarı olmadan derlenir. Visual Studio 2019 sürüm 16.0 ile 16.8 arasında derleyici C4172 uyarısını oluşturur. Visual Studio 2019 sürüm 16.9'dan başlayarak kod bir kez daha uyarı olmadan derlenir:

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 aşırı yüklenmiş bir işlevden

için bağımsız değişkeni reinterpret_cast , aşırı yüklenmiş bir işlevin adresine izin verilen bağlamlardan biri değildir. Aşağıdaki kod Visual Studio 2017'de hatasız olarak derlenmiş, ancak Visual Studio 2019'da C2440 hatasına neden olur:

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

Hatadan kaçınmak için bu senaryo için izin verilen bir yayın kullanın:

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

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

Lambda kapanışları

C++14'te lambda kapanış türleri değişmez değer değildir. Bu kuralın birincil sonucu, bir lambdanın bir constexpr değişkene atanmamasıdır. Aşağıdaki kod Visual Studio 2017'de hatasız olarak derlenmiş ancak Visual Studio 2019'da C2127 hatasına neden olur:

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

Hatadan kaçınmak için niteleyiciyi constexpr kaldırın veya uyumluluk modunu veya sonraki bir sürümle /std:c++17 değiştirin.

std::create_directory hata kodları

C++20'den P1164 koşulsuz olarak uygulandı. Bu, hedefin zaten başarısız olan bir dizin olup olmadığını denetlemek için değişir std::create_directory . Daha önce tüm ERROR_ALREADY_EXISTS tür hataları başarılı ancak dizin oluşturulmamış kodlara dönüştürüldü.

operator<<(std::ostream, nullptr_t)

Akışlara yazmak nullptr için eklenen operator<<(std::ostream, nullptr_t) LWG 2221 başına.

Daha fazla paralel algoritma

, , is_sorted_until, , is_partitioned, set_differenceset_intersection, is_heapve is_heap_until'nin is_sortedyeni paralel sürümleri.

Atomik başlatmada düzeltmeler

P0883 "Atomik başlatmayı düzeltme", varsayılan olarak başlatmak yerine içerdiğini T değer-başlatmaya dönüşürstd::atomic. Düzeltme, Microsoft standart kitaplığıyla Clang/LLVM kullanılırken etkinleştirilir. Şu anda microsoft C++ derleyicisi için devre dışı bırakılmıştır. İşlemdeki constexpr bir hata için geçici bir çözüm olarak.

remove_cvref ve remove_cvref_t

remove_cvref P0550'den ve remove_cvref_t türü özelliklerini uyguladık. Bunlar, işlev ve dizileri işaretçilere çürütmeden bir türden başvuru durumunu ve cv-nitelemini kaldırır (ve 'den farklı olarak std::decaystd::decay_t).

Özellik testi makroları

P0941R2 - özellik testi makroları , desteğiyle __has_cpp_attributetamamlandı. Özellik testi makroları tüm standart modlarda desteklenir.

Kullanıcı tarafından bildirilen oluşturucularla toplamaları yasakla

C++20 P1008R1 - kullanıcı tarafından bildirilen oluşturucularla toplamaların yasaklanması tamamlandı.

reinterpret_cast bir constexpr işlevde

bir reinterpret_cast işlevde constexpr geçersizdir. Microsoft C++ derleyicisi daha önce yalnızca bir constexpr bağlamda kullanıldıysa reddederdireinterpret_cast. Visual Studio 2019'da, tüm dil standartları modlarında, derleyici bir işlevin tanımında doğru reinterpret_cast bir constexpr şekilde tanılar. Aşağıdaki kod artık C3615 üretir:

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

Hatadan kaçınmak için değiştiriciyi constexpr işlev bildiriminden kaldırın.

basic_string aralık oluşturucu için doğru tanılama

Visual Studio 2019'da, basic_string aralık oluşturucu artık ile static_castderleyici tanılamalarını gizlemez. Aşağıdaki kod, başlatılırken içindeki olası veri wchar_t kaybına rağmen Visual Studio 2017'de char uyarı olmadan derlenir 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.

Visual Studio 2019, C4244 uyarılarını doğru şekilde yükseltir. Uyarıyı önlemek için şu örnekte gösterildiği gibi öğesini std::string başlatabilirsiniz:

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

veya altında /clr/ZW yanlış çağrılar +=-= artık doğru algılanıyor

Visual Studio 2017'de derleyicinin hataları sessizce yoksaymasına ve veya /ZWaltındaki /clr geçersiz çağrılar için kod oluşturmamasına +=-= neden olan bir hata kullanıma sunulmuştur. Aşağıdaki kod Visual Studio 2017'de hatasız olarak derlenmiş ancak Visual Studio 2019'da doğru şekilde C2845 hatasını tetiklemektedir:

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

Bu örnekteki hatayı önlemek için işlecini +=ToString() şu yöntemle kullanın: s += E::e.ToString();.

Satır içi statik veri üyeleri için başlatıcılar

ve static constexpr başlatıcıları içinde inline geçersiz üye erişimi artık doğru şekilde algılanıyor. Aşağıdaki örnek Visual Studio 2017'de hatasız olarak derleniyor, ancak Visual Studio 2019'da mod veya üzeri bir sürümde /std:c++17 C2248 hatası oluşuyor:

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

Hatayı önlemek için üyeyi X::c korumalı olarak bildirin:

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

C4800 yeniden etkinleştirildi

MSVC, C4800'e örtük dönüştürme hakkında bir performans uyarısına sahip olmak için boolkullanılır. Çok gürültülü ve gizlenemedi, bu da Visual Studio 2017'de kaldırmamıza neden oldu. Ancak Visual Studio 2017'nin yaşam döngüsü boyunca, çözdüğü yararlı durumlar hakkında birçok geri bildirim aldık. Visual Studio 2019'a, açıklayıcı C4165 ile birlikte dikkatle uyarlanmış bir C4800 getiriyoruz. Bu uyarıların her ikisi de gizlenmek kolaydır: açık bir atama kullanarak veya uygun türün 0'ını karşılaştırarak. C4800 varsayılan olmayan bir düzey 4 uyarısıdır ve C4165 varsayılan olmayan düzey 3 uyarısıdır. Her ikisi de derleyici seçeneği kullanılarak /Wall bulunabilir.

Aşağıdaki örnek altında C4800 ve C4165'i /Wallyükseltir:

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

Önceki örnekteki uyarılardan kaçınmak için kodu şu şekilde yazabilirsiniz:

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
}

Yerel sınıf üyesi işlevinin gövdesi yok

Visual Studio 2017'de C4822 uyarısı yalnızca derleyici seçeneği /w14822 açıkça ayarlandığında tetiklenir. ile /Wallgösterilmez. Visual Studio 2019'da C4822 varsayılan olmayan bir uyarıdır ve açıkça ayarlamak /w14822 zorunda kalmadan altında /Wall bulunabilir.

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

Deyimleri içeren if constexpr işlev şablonu gövdeleri

veya /std:c++latestaltında /std:c++20 Visual Studio 2019'da deyimleri olan if constexpr şablon işlev gövdelerinde ayrıştırmayla ilgili ek denetimler etkinleştirilir. Örneğin, Visual Studio 2017'de aşağıdaki kod yalnızca seçenek ayarlandıysa /permissive- C7510 üretir. Visual Studio 2019'da seçenek ayarlandığında bile /permissive aynı kod hatalara neden oluyor:

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

Hatadan kaçınmak için anahtar sözcüğünü typename bildirimine aekleyin: typename T::Type a;.

Satır içi derleme kodu lambda ifadesinde desteklenmez

Microsoft C++ ekibi kısa süre önce bir lambda içinde satır içi assembler kullanımının çalışma zamanında bozulmasına ebp (dönüş adresi yazmacı) yol açabileceği bir güvenlik sorunu hakkında bilgi edindi. Kötü amaçlı bir saldırgan bu senaryodan yararlanabilir. Satır içi derleyici yalnızca x86'da desteklenir ve satır içi derleyici ile derleyicinin geri kalanı arasındaki etkileşim zayıftır. Bu olgular ve sorunun doğası göz önünde bulundurulduğunda, bu sorunun en güvenli çözümü bir lambda ifadesi içinde satır içi derleyiciye izin vermekti.

'Vahşi doğada' bulduğumuz bir lambda ifadesi içinde satır içi assembler'ın tek kullanımı dönüş adresini yakalamaktı. Bu senaryoda, yalnızca bir derleyici iç kullanarak tüm platformlarda _ReturnAddress()dönüş adresini yakalayabilirsiniz.

Aşağıdaki kod, Visual Studio 2017 15.9 ve sonraki Visual Studio sürümlerinde C7553 üretir:

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

Hatadan kaçınmak için, derleme kodunu aşağıdaki örnekte gösterildiği gibi adlandırılmış bir işleve taşıyın:

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

Yineleyici hata ayıklama ve std::move_iterator

Yineleyici hata ayıklama özelliği düzgün bir şekilde çıkarılması std::move_iteratoröğretildi. Örneğin, std::copy(std::move_iterator<std::vector<int>::iterator>, std::move_iterator<std::vector<int>::iterator>, int*) artık hızlı yol ile etkileşime memcpy geçebilirsiniz.

xkeycheck.h> anahtar sözcük zorlaması için <düzeltmeler

Xkeycheck.h> dosyasındaki <standart kitaplığın bir anahtar sözcüğü değiştiren makrolar için zorlaması düzeltildi. Kitaplık artık genel bir ileti yerine algılanan gerçek sorun anahtar sözcüğünü yayar. Ayrıca C++20 anahtar sözcüklerini de destekler ve IntelliSense'i rastgele anahtar sözcüklerin makro olduğunu söyleyerek kandırmaktan kaçınıyor.

Ayırıcı türleri artık kullanım dışı bırakılmıyor

std::allocator<void>, std::allocator::size_typeve std::allocator::difference_type artık kullanım dışı değildir.

Dize dönüştürmelerini daraltma için doğru uyarı

static_caststd::string Standart tarafından çağrılmamış olan ve yanlışlıkla C4244 daraltma uyarılarını bastıran bir mahmuz kaldırıldı. Çağrısı std::string::string(const wchar_t*, const wchar_t*) yapmaya çalışırken C4244'i içine daraltma wchar_t konusunda düzgün bir charşekilde yayma girişiminde bulunur.

Dosya sistemi> doğruluğu için <çeşitli düzeltmeler

  • Bir dizinin son yazma zamanını değiştirmeye çalışırken başarısız olan hata düzeltildi std::filesystem::last_write_time .
  • Oluşturucu std::filesystem::directory_entry artık var olmayan bir hedef yol sağlandığında bir özel durum oluşturmak yerine başarısız bir sonucu depolar.
  • std::filesystem::create_directory 2 parametreli sürüm, temel CreateDirectoryExW alınan işlev bir symlink olduğunda existing_p kullanacağı copy_symlink için 1 parametreli sürümü çağıracak şekilde değiştirildi.
  • std::filesystem::directory_iterator artık bozuk bir symlink bulunduğunda başarısız olmaz.
  • std::filesystem::space şimdi göreli yolları kabul eder.
  • std::filesystem::path::lexically_relativeartık LWG 3096 olarak bildirilen sondaki eğik çizgiyle karıştırılmamaktadır.
  • içinde std::filesystem::create_symlinkeğik çizgilerle yolları reddetme konusunda çalıştıCreateSymbolicLinkW.
  • Windows 10 LTSB 1609'da mevcut olan ancak aslında dosyaları silemeyen POSIX silme modu delete işlevine geçici bir çözüm bulundu.
  • Ve std::boyer_moore_searcherstd::boyer_moore_horspool_searcher kopya oluşturucuları ve kopyalama atama işleçleri artık öğeleri kopyalar.

Windows 8 ve sonraki sürümlerde paralel algoritmalar

Paralel algoritmalar kitaplığı artık her zaman Windows 7 ve önceki sahte sürümleri kullanmak yerine Windows 8 ve sonraki sürümlerde gerçek WaitOnAddress aileyi düzgün bir şekilde kullanıyor.

std::system_category::message() Boşluk

std::system_category::message() şimdi döndürülen iletiden sondaki boşluğu kırpıyor.

std::linear_congruential_engine sıfıra bölme

0'a bölmeyi tetiklemesine neden std::linear_congruential_engine olabilecek bazı koşullar düzeltildi.

Yineleyiciyi açma düzeltmeleri

Bazı yineleyici kaldırma makineleri ilk olarak Visual Studio 2017 15.8'de programcı-kullanıcı tümleştirmesi için kullanıma sunuldu. Vs 2017 15.8'de C++ Ekip Blogu makalesinde STL Özellikleri ve Düzeltmeleri açıklanmıştır. Bu makine artık standart kitaplık yineleyicilerinden türetilen yineleyicileri açmaz. Örneğin, türetilen std::vector<int>::iterator ve davranışı özelleştirmeye çalışan bir kullanıcı artık bir işaretçinin davranışı yerine standart kitaplık algoritmalarını çağırırken özelleştirilmiş davranışını alır.

Sıralanmamış kapsayıcı reserve işlevi artık LWG 2156'da açıklandığı gibi N öğeleri için ayrılmıştır.

Zaman işleme

  • Daha önce, eşzamanlılık kitaplığına geçirilen bazı zaman değerleri taşıyordu, örneğin, condition_variable::wait_for(seconds::max()). Artık düzeltildi, taşmalar rastgele 29 günlük bir döngüde (temel win32 API'leri tarafından kabul edilen uint32_t milisaniye taştığında) davranışı değiştirdi.

  • <ctime> üst bilgisi artık ve ad alanında stddoğru şekilde bildirir timespectimespec_get ve bunları genel ad alanında da bildirir.

Kapsayıcılar için çeşitli düzeltmeler

  • Geliştirilmiş IntelliSense deneyimi için birçok standart kitaplık iç kapsayıcı işlevi yapılmıştır private . MSVC'nin sonraki sürümlerinde üyeleri beklendiği gibi private işaretlemek için daha fazla düzeltme.

  • , mapve unordered_mapgibi listdüğüm tabanlı kapsayıcıların bozulmasına neden olan özel durum güvenliği doğruluğu sorunlarını giderdik. Bir propagate_on_container_copy_assignment veya propagate_on_container_move_assignment yeniden atama işlemi sırasında, kapsayıcının sentinel düğümünü eski ayırıcıyla serbest bırakmalı, eski ayırıcı üzerinde POCCA/POCMA atamasını yapacak ve ardından sentinel düğümünü yeni ayırıcıdan almayı deneyeceğiz. Bu ayırma başarısız olursa kapsayıcı bozulmuştur. Sentinel düğümüne sahip olmak sabit bir veri yapısı olduğundan yok bile edilemedi. Bu kod, mevcut sentinel düğümünü yok etmeden önce kaynak kapsayıcının ayırıcısını kullanarak yeni sentinel düğümünü oluşturmak için düzeltildi.

  • Kapsayıcılar, bildirilen ayırıcılar için bile ayırıcıları , ve propagate_on_container_swapdeğerlerine propagate_on_container_copy_assignmentgöre her zaman kopyalayıp taşıyacak/değiştirecek şekilde düzeltildiis_always_equal. propagate_on_container_move_assignment

  • Kapsayıcı birleştirme ve rvalue kapsayıcılarını kabul eden üye işlevleri ayıklama için aşırı yüklemeler eklendi. Daha fazla bilgi için bkz. P0083 "Haritalar Ve Kümeleri Ekleme"

std::basic_istream::read \n' işleminin \r\n`` =>işlenmesi

std::basic_istream::read , sağlanan arabelleğin bölümlerine işlemenin \r\n\n bir parçası olarak geçici olarak yazılmayacak şekilde düzeltildi. Bu değişiklik, 4K'dan büyük okumalar için Visual Studio 2017 15.8'de elde edilen performans avantajlarından bazılarını verir. Ancak karakter başına üç sanal çağrıdan kaçınmaya yönelik verimlilik iyileştirmeleri hala mevcuttur.

std::bitset Oluşturucu

Oluşturucu std::bitset artık büyük bit kümeleri için birleri ve sıfırları ters sırada okumaz.

std::pair::operator= Regresyon

LWG 2729 "Üzerinde eksik SFINAEstd::pair::operator=" uygulanırken ortaya çıkan atama işlecindeki std::pair regresyon düzeltildi. Artık dönüştürülebilir std::pair türleri yeniden doğru kabul eder.

Için çıkarılmayan bağlamlar add_const_t

ve ilgili işlevlerin çıkarılmayan bir bağlam olması gereken küçük tür özellikleri hatasını add_const_t düzeltildi. Başka bir deyişle, add_const_t yerine için typename add_const<T>::typeconst Tbir diğer ad olmalıdır.

16.1'de uyumluluk iyileştirmeleri

char8_t

P0482r6. C++20, UTF-8 kod birimlerini temsil etmek için kullanılan yeni bir karakter türü ekler. u8C++20'deki dize değişmez değerleri, daha önce olduğu gibi yerine const char[N]türüne const char8_t[N] sahiptir. N2231'de C standardı için benzer değişiklikler önerilmiştir. Geriye dönük uyumluluk düzeltme önerileri char8_t P1423r3'te verilmiştir. Microsoft C++ derleyicisi, derleyici seçeneğini belirttiğinizde /Zc:char8_t Visual Studio 2019 sürüm 16.1'de için char8_t destek ekler. aracılığıyla /Zc:char8_t-C++17 davranışına geri döndürülebilir. IntelliSense'i destekleyen EDG derleyicisi henüz Visual Studio 2019 sürüm 16.1'de desteklememektedir. Gerçek derlemeyi etkilemeyen, yalnızca IntelliSense hataları görebilirsiniz.

Örnek

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

std::type_identity metafunction ve std::identity işlev nesnesi

P0887R1 type_identity. Kullanım dışı bırakılan std::identity sınıf şablonu uzantısı kaldırıldı ve C++20 std::type_identity meta işlevi ve std::identity işlev nesnesiyle değiştirildi. Her ikisi de yalnızca (/std:c++20 Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) altında /std:c++latest kullanılabilir.

Aşağıdaki örnek, Visual Studio 2017'de için std::identity kullanımdan kaldırma uyarısı C4996 (type_traits> içinde <tanımlanır) oluşturur:

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

Aşağıdaki örnekte, yeni ile birlikte yeninin std::identity (işlevsel> olarak <tanımlanmış) nasıl kullanılacağı gösterilmektedirstd::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);

Genel lambdalar için söz dizimi denetimleri

Yeni lambda işlemcisi, /std:c++latest/std:c++20 (Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) veya Visual Studio 2019 sürüm 16.9 veya sonraki sürümlerde (daha önce Visual Studio 2019 sürüm 16.3'te başlarken /experimental:newLambdaProcessor kullanılabilir) diğer /Zc:lambda dil modlarında genel lambdalarda bazı uyumluluk modu söz dizimsel denetimleri etkinleştirir.

Eski lambda işlemcisi bu örneği uyarı olmadan derler, ancak yeni lambda işlemcisi C2760 hatasını üretir:

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

Bu örnekte, derleyici tarafından zorlanan doğru söz dizimi gösterilmektedir:

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

İşlev çağrıları için bağımsız değişkene bağımlı arama

P0846R0 (C++20) Açık şablon bağımsız değişkenleriyle işlev çağrısı ifadeleri için bağımsız değişkene bağımlı arama yoluyla işlev şablonlarını bulma özelliği artırıldı. /std:c++latest (veya /std:c++20 Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) gerektirir.

Belirlenen başlatma

P0329R4 (C++20) Belirlenen başlatma, söz dizimi kullanılarak belirli üyelerin toplu başlatmada seçilmesini Type t { .member = expr } sağlar. /std:c++latest (veya /std:c++20 Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) gerektirir.

Sabit temel alınan türüne sabit numaralandırma dönüştürmesinin derecelendirmesi

Derleyici artık sabit listesi dönüştürmelerini N4800 11.3.3.2 Sıralama örtük dönüştürme sıralarına (4.2) göre sıralar:

  • Temel türü temel alınan türüne sabitlenmiş bir numaralandırmayı yükselten dönüştürme, ikisi farklıysa yükseltilen temel türe yükselten bir dönüştürmeden daha iyidir.

Bu dönüştürme derecelendirmesi Visual Studio 2019 sürüm 16.1'e doğru uygulanmadı. Uyumlu davranış, aşırı yükleme çözümleme davranışını değiştirebilir veya daha önce algılanmadığı bir belirsizlik ortaya çıkarabilir.

Bu derleyici davranışı değişikliği tüm /std modlar için geçerlidir ve hem kaynak hem de ikili hataya neden olan bir değişikliktir.

Aşağıdaki örnek, 16.1 ve sonraki sürümlerde derleyici davranışının nasıl değiştiğini gösterir:

#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{});
}

Yeni ve güncelleştirilmiş standart kitaplık işlevleri (C++20)

  • starts_with()ve ends_with()basic_string_viewve içinbasic_string.
  • İlişkili kapsayıcılar için contains().
  • list ve forward_list için remove(), remove_if() ve unique() artık size_type değerini döndürüyor.
  • shift_left()ve shift_right() algoritmaya <>eklendi.

16.2'de uyumluluk iyileştirmeleri

noexceptconstexpr işlevleri

constexpr işlevleri artık sabit ifadede kullanıldığında varsayılan olarak dikkate alınmaz noexcept . Bu davranış değişikliği Çekirdek Çalışma Grubu (CWG) CWG 1351 çözümünden gelir ve içinde /permissive-etkindir. Aşağıdaki örnek Visual Studio 2019 sürüm 16.1 ve önceki sürümlerde derlenmiş ancak Visual Studio 2019 sürüm 16.2'de C2338 üretir:

constexpr int f() { return 0; }

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

Hatayı düzeltmek için ifadeyi noexcept işlev bildirimine ekleyin:

constexpr int f() noexcept { return 0; }

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

Farklı sabit listesi türlerine sahip ikili ifadeler

C++20 işlenenlerde normal aritmetik dönüştürmeleri kullanım dışı bırakmıştır; burada:

  • İşlenenlerden biri sabit listesi türündedir ve

  • diğeri farklı bir numaralandırma türünde veya kayan nokta türündedir.

Daha fazla bilgi için bkz . P1120R0.

Visual Studio 2019 sürüm 16.2 ve sonraki sürümlerinde, derleyici seçeneği etkinleştirildiğinde /std:c++latest (/std:c++20 Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) aşağıdaki kod düzey 4 C5054 uyarısı oluşturur:

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

Uyarıyı önlemek için ikinci işleneni dönüştürmek için kullanın static_cast :

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

Numaralandırma ile kayan nokta türü arasında ikili işlem kullanmak, derleyici seçeneği etkinleştirildiğinde /std:c++latest (/std:c++20 Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) artık düzey 1 C5055 uyarısıdır:

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

Uyarıyı önlemek için ikinci işleneni dönüştürmek için kullanın static_cast :

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

Dizilerin eşitlik ve ilişkisel karşılaştırmaları

Dizi türünün iki işleneni arasındaki eşitlik ve ilişkisel karşılaştırmalar C++20 'de (P1120R0) kullanım dışıdır. Başka bir deyişle, iki dizi arasındaki karşılaştırma işlemi (derece ve kapsam benzerliklerine rağmen) artık bir uyarıdır. Visual Studio 2019 sürüm 16.2 ve sonraki sürümlerinde, derleyici seçeneği etkinleştirildiğinde /std:c++latest (/std:c++20 Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde) aşağıdaki kod düzey 1 uyarı C5056 üretir:

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

Uyarıyı önlemek için ilk öğelerin adreslerini karşılaştırabilirsiniz:

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

İki dizinin içeriğinin eşit olup olmadığını belirlemek için işlevini kullanın std::equal :

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

Uzay gemisi işleci tanımlamanın ve üzerindeki == etkisi !=

Yalnızca uzay gemisi işlecinin (<=>) tanımı, uzay gemisi işleci (P1185R2) olarak = default işaretlenmediği sürece veya içeren ==!= ifadeleri artık yeniden yazmayacak. Aşağıdaki örnek Visual Studio 2019 RTW ve sürüm 16.1'de derlenmiş ancak Visual Studio 2019 sürüm 16.2'de C2678 üretir:

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

Hatadan kaçınmak için bunu varsayılan olarak tanımlayın operator== veya bildirin:

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

Standart Kitaplık geliştirmeleri

  • <sabit/bilimsel duyarlık ile charconv>to_chars() . (Genel duyarlık şu anda 16.4 için planlanıyor.)
  • P0020R6: atomic<float>, atomic<double>,atomic<long double>
  • P0463R1: endian
  • P0482R6: Kitaplık Desteğichar8_t
  • P0600R1: [[nodiscard]] STL için, Bölüm 1
  • P0653R2:to_address()
  • P0754R2: <sürüm>
  • P0771R1: noexceptstd::function's move oluşturucu için

İlişkili kapsayıcılar için en iyi karşılaştırıcılar

, , mapmultisetve multimap içindeki setarama ve ekleme kodu, daha küçük kod boyutu için birleştirildi. Ekleme işlemleri artık daha önce yapılan arama işlemleriyle aynı şekilde karşılaştırma const funktöründe küçüktür karşılaştırmasını çağırır. Aşağıdaki kod Visual Studio 2019 sürüm 16.1 ve önceki sürümlerinde derlenmiş ancak Visual Studio 2019 sürüm 16.2'de C3848'i yükseltir:

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

}

Hatadan kaçınmak için karşılaştırma işlecini constyapın:

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

Visual Studio 2019 sürüm 16.3'te uyumluluk geliştirmeleri

Kaldırılacak akış ayıklama işleçleri char*

İşaretçiden karaktere akış ayıklama işleçleri kaldırıldı ve karakter dizisi (P0487R1 başına) için ayıklama işleçleri tarafından değiştirildi. WG21, kaldırılan aşırı yüklemeleri güvenli değil olarak kabul eder. /std:c++20 Veya /std:c++latest modunda, aşağıdaki örnek artık C2679 üretir:

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

Hatayı önlemek için ayıklama işlecini bir char[] değişkenle kullanın:

#include <iostream>
#include <iomanip>

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

Yeni anahtar sözcükler requires ve concept

Yeni anahtar sözcükler requires ve concept Microsoft C++ derleyicisine eklendi. Ya da modunda /std:c++20/std:c++latest tanımlayıcı olarak birini kullanmayı denerseniz, derleyici bir söz dizimi hatası belirtmek için C2059'ı yükseltir.

Tür adları olarak oluşturuculara izin verilmiyor

Derleyici artık oluşturucu adlarını, bir sınıf şablonu özelleştirmesinin diğer adından sonra nitelenmiş bir adla göründüklerinde, eklenen-sınıf-adları olarak değerlendirmez. Daha önce oluşturucular, diğer varlıkları bildirmek için bir tür adı olarak kullanılabilirdi. Aşağıdaki örnek artık C3646 üretir:

#include <chrono>

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

Hatayı önlemek için burada gösterildiği gibi bildirin TotalDuration :

#include <chrono>

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

İşlevleri daha sıkı denetleme extern "C"

İşlev extern "C" farklı ad alanlarına bildirildiyse, Microsoft C++ derleyicisinin önceki sürümleri bildirimlerin uyumlu olup olmadığını denetlemedi. Visual Studio 2019 sürüm 16.3 ve sonraki sürümlerde, derleyici uyumluluğu denetler. Modunda /permissive- , aşağıdaki kod C2371 ve C2733 hataları üretir:

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

Önceki örnekteki hataları önlemek için, her iki bildiriminde fde tutarlı olarak yerine BOOL kullanınbool.

Standart Kitaplık geliştirmeleri

Standart olmayan stdexcpt.h> ve <typeinfo.h> üst bilgileri <kaldırıldı. Bunları içeren kod bunun yerine sırasıyla standart üst bilgi özel durumunu> ve <typeinfo'ları <>içermelidir.

Visual Studio 2019 sürüm 16.4'te uyumluluk geliştirmeleri

içindeki nitelenmiş kimlikler için iki aşamalı ad aramasının daha iyi uygulanması /permissive-

İki aşamalı ad araması, şablon gövdelerinde kullanılan bağımlı olmayan adların tanım zamanında şablona görünür olmasını gerektirir. Daha önce, şablon örneği oluşturulurken bu tür adlar bulunabilirdi. Bu değişiklik, MSVC'de bayrağı altında /permissive- taşınabilir ve uyumlu kod yazmayı kolaylaştırır.

Bayrak ayarlanmış Visual Studio 2019 sürüm 16.4'te/permissive-, şablon tanımlandığında f<T> görünür olmadığından aşağıdaki örnek bir hata N::f oluşturur:

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

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

Bu hata genellikle aşağıdaki örnekte gösterildiği gibi eksik üst bilgiler veya ileri bildirim işlevleri veya değişkenler dahil edilerek düzeltilebilir:

namespace N {
    int f();
}

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

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

Tamsayı sabit ifadelerinin null işaretçiye örtük olarak dönüştürülmesi

MSVC derleyicisi artık uyumluluk modunda (/permissive-) CWG Sorunu 903'i uyguluyor. Bu kural, tamsayı sabit ifadelerinin (tamsayı değişmez değeri '0' hariç) null işaretçi sabitlerine örtük olarak dönüştürülmesine izin vermemektedir. Aşağıdaki örnek C2440'ı uyumluluk modunda üretir:

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 *'
}

Hatayı düzeltmek için yerine falsekullanınnullptr. Değişmez değer 0'a hala izin verilir:

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

Tamsayı değişmez değer türleri için standart kurallar

Uyumluluk modunda (tarafından /permissive-etkinleştirilir), MSVC tamsayı değişmez değer türleri için standart kuralları kullanır. Içine sığmayacak signed int kadar büyük ondalık değişmez değerlere daha önce türü unsigned intverilmişti. Şimdi bu tür değişmez değerlere sonraki en büyük signed tamsayı türü verilir: long long. Ayrıca, 'll' soneki olan ve bir signed türe sığamayacak kadar büyük olan değişmez değerlere türü unsigned long longverilir.

Bu değişiklik farklı uyarı tanılamalarının oluşturulmasına ve değişmez değerlerdeki aritmetik işlemler için davranış farklılıklarına yol açabilir.

Aşağıdaki örnekte Visual Studio 2019 sürüm 16.4'teki yeni davranış gösterilmektedir. Değişkeni i artık türünde unsigned intolduğundan uyarı oluşturulur. Değişkenin j yüksek sıralı bitleri 0 olarak ayarlanır.

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
}

Aşağıdaki örnek, eski davranışın nasıl tutulduğunu ve uyarıların ve çalışma zamanı davranışı değişikliğinin nasıl önlenip önleneceğini gösterir:

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

Şablon parametrelerini gölgeleyen işlev parametreleri

MSVC derleyicisi artık bir işlev parametresi şablon parametresini gölgelediğinde bir hata oluşturur:

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

Hatayı düzeltmek için parametrelerden birinin adını değiştirin:

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

Tür özelliklerinin kullanıcı tarafından sağlanan uzmanlıkları

Standart'ın meta.rqmts alt koduna uygun olarak, MSVC derleyicisi artık ad alanında std belirtilen type_traits şablonlardan birinin kullanıcı tanımlı özelleştirmesini bulduğunda bir hata oluşturur. Aksi belirtilmedikçe, bu tür özelleştirmeler tanımsız davranışla sonuçlanır. Aşağıdaki örnekte kuralı ihlal ettiği ve static_assert C2338 hatasıyla başarısız olduğu için tanımsız bir davranış vardır.

#include <type_traits>
struct S;

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

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

Hatadan kaçınmak için, tercih edilen type_traitöğesinden devralan bir yapı tanımlayın ve şu konularda uzmanlaşın:

#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");

Derleyici tarafından sağlanan karşılaştırma işleçleri değişiklikleri

MSVC derleyicisi artık veya /std:c++latest seçeneği etkinleştirildiğinde /std:c++20 P1630R1 başına karşılaştırma işleçlerinde aşağıdaki değişiklikleri uygular:

Derleyici artık olmayan bir dönüş türü içeriyorsa kullanarak operator== ifadeleri yeniden yazmıyor bool. Aşağıdaki kod artık C2088 hatasını üretir:

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
}

Hatayı önlemek için gerekli işleci açıkça tanımlamanız gerekir:

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

Derleyici artık birleşim benzeri bir sınıfın üyesiyse varsayılan bir karşılaştırma işleci tanımlamaz. Aşağıdaki örnek şimdi C2120 hatasını oluşturur:

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

Hatadan kaçınmak için işleç için bir gövde tanımlayın:

#include <compare>

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

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

Sınıf bir başvuru üyesi içeriyorsa derleyici artık varsayılan bir karşılaştırma işleci tanımlamaz. Aşağıdaki kod artık C2120 hatasını üretir:

#include <compare>

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

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

Hatadan kaçınmak için işleç için bir gövde tanımlayın:

#include <compare>

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

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

Visual Studio 2019 sürüm 16.5'te uyumluluk geliştirmeleri

Başlatıcısı olmayan açık özelleştirme bildirimi bir tanım değildir

altında /permissive-, MSVC artık başlatıcıları olmayan açık özelleştirme bildirimlerinin tanım olmadığını belirten standart bir kural uygular. Daha önce, bildirim varsayılan başlatıcısı olan bir tanım olarak kabul edilirdi. Bu davranışa bağlı bir program artık çözümlenmemiş simgelere sahip olabileceğinden, etki bağlantı zamanında gözlemlenebilir. Bu örnek şimdi bir hatayla sonuçlanır:

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.

Sorunu çözmek için bir başlatıcı ekleyin:

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

Önişlemci çıkışı yeni çizgileri korur

Deneysel önişlemci artık veya /E ile /experimental:preprocessorkullanırken /P yeni çizgileri ve boşlukları korur.

Bu örnek kaynak göz önünde bulundurulduğunda,

#define m()
line m(
) line

Önceki çıktısı /E :

line line
#line 2

Yeni çıktısı /E şu şekildedir:

line
 line

import ve module anahtar sözcükler bağlama bağımlıdır

P1857R1 import başına ve module önişlemci yönergelerinin söz diziminde yeni kısıtlamalar vardır. Bu örnek artık derlenmez:

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

Sorunu çözmek için içeri aktarma işlemini aynı satırda tutun:

import m; // OK

ve'nin std::weak_equality kaldırılması std::strong_equality

P1959R0 birleştirilmesi için derleyicinin ve std::strong_equality türlerine yönelik davranışları ve başvuruları kaldırması std::weak_equality gerekir.

Bu örnekteki kod artık derlenmez:

#include <compare>

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

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

Örnek şimdi şu hatalara yol açar:

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'

Sorunu çözmek için yerleşik ilişkisel işleçleri tercih etmek için güncelleştirin ve kaldırılan türleri değiştirin:

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

TLS Guard değişiklikleri

Daha önce DLL'lerdeki iş parçacığı yerel değişkenleri doğru başlatılmamıştı. DLL'yi yükleyen iş parçacığı dışında, DLL yüklenmeden önce var olan iş parçacıklarında ilk kullanımdan önce başlatılmadı. Bu hata düzeltildi. Böyle bir DLL'deki iş parçacığı yerel değişkenleri, bu tür iş parçacıklarında ilk kullanımlarından hemen önce başlatılır.

İş parçacığı yerel değişkenlerinin kullanımlarında başlatma testinin bu yeni davranışı, derleyici seçeneği kullanılarak /Zc:tlsGuards- devre dışı bırakılabilir. Veya özniteliğini [[msvc:no_tls_guard]] belirli iş parçacığı yerel değişkenlerine ekleyerek.

Silinen işlevlere çağrının daha iyi tanısını koyma

Derleyicimiz daha önce silinen işlevlere yapılan çağrılar konusunda daha izinliydi. Örneğin, çağrılar bir şablon gövdesi bağlamında gerçekleşseydi, çağrıyı tanılamazdık. Ayrıca, silinen işlevlere yönelik çağrıların birden çok örneği varsa, yalnızca bir tanılama işlemi yaparız. Şimdi her biri için bir tanılama işlemi yapıyoruz.

Yeni davranışın bir sonucu küçük bir hataya neden olabilir: Silinen işlevi çağıran kod, kod oluşturma için hiç gerekmeseydi tanılanmazdı. Şimdi ön tanıyı koyacağız.

Bu örnekte şu anda bir hata oluşturan kod gösterilmektedir:

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

Sorunu çözmek için silinen işlevlere yapılan çağrıları kaldırın:

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

Visual Studio 2019 sürüm 16.6'da uyumluluk geliştirmeleri

Standart kitaplık akışları yanlış kodlanmış karakter türlerinin eklenmesini reddeder

Geleneksel olarak, içine bir wchar_tstd::ostreamekleme ve char16_t ekleme veya veya std::wostreamchar32_tstd::ostream içine ekleme, integral değerini verir. Bu karakter türlerine işaretçiler eklemek işaretçi değerini verir. Programcılar her iki olayı da sezgisel bulmaz. Genellikle standart kitaplığın bunun yerine karakteri veya null ile sonlandırılan karakter dizesini dönüştürmesini ve sonucun çıkışını yapmasını beklerler.

C++20 teklifi P1423R3 , akış ve karakter veya karakter işaretçisi türlerinin bu birleşimleri için silinmiş akış ekleme işleci aşırı yüklemeleri ekler. veya /std:c++latestaltında /std:c++20 aşırı yüklemeler, bu eklemeleri istenmeyen bir şekilde davranmak yerine kötü biçimlendirilmiş hale getirir. Derleyici, bir tane bulunduğunda C2280 hatasını oluşturur. Eski davranışı geri yüklemek için için 1 "kaçış taraması" makrosunu _HAS_STREAM_INSERTION_OPERATORS_DELETED_IN_CXX20 tanımlayabilirsiniz. (Teklif, için char8_takış ekleme işleçlerini de siler. Destek eklediğimizde char8_t standart kitaplığımız benzer aşırı yüklemeler uyguladı, bu nedenle "yanlış" davranış hiçbir zaman için char8_tkullanılabilir olmamıştır.)

Bu örnek, bu değişiklikle ilgili davranışı gösterir:

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

Kod şimdi şu tanılama iletilerini üretir:

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

Karakter türlerini öğesine veya işaretçiden const void*karaktere türlerini unsigned intöğesine dönüştürerek tüm dil modlarında eski davranışın etkisini elde edebilirsiniz:

#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"
}

için dönüş türü std::pow() değiştirildi std::complex

Daha önce, işlev şablonunun std::pow() dönüş türü için yükseltme kurallarının MSVC uygulaması yanlıştı. Örneğin, daha önce pow(complex<float>, int) döndürdü complex<float>. Şimdi doğru şekilde döndürür complex<double>. Düzeltme, Visual Studio 2019 sürüm 16.6'daki tüm standart modları için koşulsuz olarak uygulanmıştır.

Bu değişiklik derleyici hatalarının nedeni olabilir. Örneğin, daha önce bir floatile çarpabilirsinizpow(complex<float>, int). complex<T> operator* Aynı türdeki bağımsız değişkenleri beklediğinden, aşağıdaki örnek şimdi derleyici hatası C2676'yı yayar:

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

Birçok olası düzeltme vardır:

  • Multiplicand türünü float olarak doubledeğiştirin. Bu bağımsız değişken, tarafından powdöndürülen türle eşleşecek şekilde doğrudan öğesine complex<double> dönüştürülebilir.

  • ifadesini söyleyerek complex<float>{pow(ARG, ARG)}sonucunu pow olarak complex<float> daraltın. Daha sonra bir float değerle çarpmaya devam edebilirsiniz.

  • yerine int öğesine geçirin.powfloat Bu işlem daha yavaş olabilir.

  • Bazı durumlarda, tamamen kaçınabilirsiniz pow . Örneğin, pow(cf, -1) bölme ile değiştirilebilir.

switch C için uyarılar

Visual Studio 2019 sürüm 16.6 ve sonraki sürümlerinde, derleyici C olarak derlenen kod için önceden var olan bazı C++ uyarıları uygular. Şu uyarılar artık farklı düzeylerde etkinleştirilmiştir: C4060, C4061, C4062, C4063, C4064, C4065, C4808 ve C4809. C4065 ve C4060 uyarıları C'de varsayılan olarak devre dışıdır.

Uyarılar eksik case deyimler, tanımsız enumve hatalı bool switch deyimleri (çok fazla durum içeren deyimler) üzerinde tetiklenir. Örneğin:

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

Bu kodu düzeltmek için yedekli default olayı kaldırın:

#include <stdbool.h>

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

Bildirimlerde typedef adsız sınıflar

Visual Studio 2019 sürüm 16.6 ve sonraki sürümlerinde bildirimlerin typedef davranışı P1766R1 uygun olacak şekilde kısıtlanmıştır. Bu güncelleştirmeyle, bir typedef bildirim içindeki adlandırılmamış sınıfların aşağıdakilerden başka üyesi olamaz:

  • varsayılan üye başlatıcısı olmayan statik olmayan veri üyeleri,
  • üye sınıfları veya
  • üye sabit listeleri.

Aynı kısıtlamalar iç içe geçmiş her sınıfa özyinelemeli olarak uygulanır. Kısıtlama, bağlantı amacıyla adları olan typedef yapıların basitliğini sağlamaya yöneliktir. Derleyici bağlantı için ada gelmeden önce hiçbir bağlantı hesaplaması gerekmeyen typedef basit olmaları gerekir.

Bu değişiklik, derleyicinin tüm standart modlarını etkiler. Varsayılan (/std:c++14) ve /std:c++17 modlarda, derleyici uyumsuz kod için C5208 uyarısını yayar. belirtilirse /permissive- , derleyici C5208 uyarısını altında /std:c++14 bir hata olarak ve altında /std:c++17C7626 hatasını yayar. Derleyici, veya /std:c++latest belirtildiğinde /std:c++20 uyumsuz kod için C7626 hatasını yayar.

Aşağıdaki örnek, adsız yapılarda artık izin verilmeyen yapıları gösterir. Belirtilen standartlar moduna bağlı olarak, C5208 veya C7626 hataları veya uyarıları yayılır:

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;

Yukarıdaki kod, adlandırılmamış sınıfa bir ad vererek düzeltilebilir:

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

C++/CLI'da varsayılan bağımsız değişken içeri aktarma

Artan sayıda API'nin .NET Core'da varsayılan bağımsız değişkenleri vardır. Bu nedenle artık C++/CLI'da varsayılan bağımsız değişken içeri aktarmayı destekliyoruz. Bu değişiklik, aşağıdaki örnekte olduğu gibi birden çok aşırı yüklemenin bildirildiği mevcut kodu bozabilir:

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

Bu sınıf C++/CLI'ye aktarıldığında aşırı yüklemelerden birine yapılan bir çağrı hataya neden olur:

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

Her iki aşırı yükleme de bu bağımsız değişken listesiyle eşleştiğinden derleyici C2668 hatasını yayar. İkinci aşırı yüklemede, ikinci bağımsız değişken varsayılan bağımsız değişken tarafından doldurulur. Bu sorunu geçici olarak çözmek için yedekli aşırı yüklemeyi (1) silebilirsiniz. Alternatif olarak, tam bağımsız değişken listesini kullanın ve varsayılan bağımsız değişkenleri açıkça sağlayın.

Visual Studio 2019 sürüm 16.7'de uyumluluk geliştirmeleri

önemsiz olarak kopyalanabilir tanımdır

C++20' nin tanımı önemsiz olarak kopyalanabilir olarak değiştirildi. Bir sınıfın nitelenmiş türe sahip statik olmayan bir veri üyesi volatile olduğunda, artık derleyici tarafından oluşturulan kopyalama veya taşıma oluşturucularının ya da kopyalama veya taşıma atama işlecinin önemsiz olmadığı anlamına gelir. C++ Standart komitesi bu değişikliği geriye dönük olarak Hata Raporu olarak uygulamıştır. MSVC'de derleyici davranışı veya /std:c++latestgibi /std:c++14 farklı dil modlarında değişmez.

İşte yeni davranış örneği:

#include <type_traits>

struct S
{
    volatile int m;
};

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

Bu kod, Visual Studio 2019 sürüm 16.7'den önceki MSVC sürümlerinde derlenemez. Bu değişikliği algılamak için kullanabileceğiniz varsayılan olmayan bir derleyici uyarısı vardır. Yukarıdaki kodu kullanarak cl /W4 /w45220derlerseniz aşağıdaki uyarıyı görürsünüz:

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`

İşaretçiden üyeye ve dize değişmez değer dönüştürmeleri bool daraltılıyor

C++ Standard komitesi yakın zamanda daraltma dönüştürmesi olarak kabul T*bool edilen Hata Raporu P1957R2 kabul etti. MSVC, uygulamasında daha önce daraltma olarak tanılanan T*bool ancak dize değişmez değeri bool veya işaretçiden üyeye booldönüştürmeyi tanılamayan bir hatayı düzeltti.

Aşağıdaki program Visual Studio 2019 sürüm 16.7'de kötü oluşturulmuştur:

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
}

Bu kodu düzeltmek için öğesine açık karşılaştırmalar nullptrekleyin veya daraltma dönüştürmelerinin hatalı biçimlendirilmiş olduğu bağlamlardan kaçının:

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_tyalnızca doğrudan başlatma olarak dönüştürülebilir bool

C++11'de, nullptr yalnızca doğrudan dönüştürme olarak dönüştürülebilirbool; örneğin, braced initializer-list kullanarak bir bool başlatma yaptığınızda. Bu kısıtlama MSVC tarafından hiçbir zaman uygulanmadı. MSVC artık altında /permissive-kuralı uygular. Örtük dönüştürmeler artık kötü biçimlendirilmiş olarak tanılanıyor. Doğrudan başlatma bool b(nullptr) geçerli olduğundan bağlamsal dönüştürmeye bool hala izin verilir.

Çoğu durumda, hata şu örnekte gösterildiği gibi ile falsedeğiştirilerek nullptr düzeltilebilir:

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
}

Eksik başlatıcılarla dizi başlatmaları için uyumlu başlatma davranışı

Daha önce MSVC' nin eksik başlatıcıları olan dizi başlatmaları için uyumsuz davranışı vardı. MSVC her zaman başlatıcısı olmayan her dizi öğesi için varsayılan oluşturucu olarak adlandırılır. Standart davranış, her öğeyi boş bir braced-initializer-list ({} ile başlatmaktır). Boş bir braced-initializer-list için başlatma bağlamı, açık oluşturuculara çağrılara izin vermeyen kopya başlatmadır. Çalışma zamanı farklılıkları da olabilir, çünkü başlatma kullanımı {} varsayılan oluşturucu yerine bir kullanan bir std::initializer_listoluşturucuyu çağırabilir. Uyumlu davranış altında /permissive-etkinleştirilir.

Değiştirilen davranışa bir örnek aşağıda verilmiştir:

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şırı yüklenmiş adlara sahip sınıf üyelerinin başlatılması doğru sıralanmış

Bir tür adı da veri üyesinin adı olarak aşırı yüklendiğinde sınıf veri üyelerinin iç gösteriminde bir hata belirledik. Bu hata, toplu başlatma ve üye başlatma sırasında tutarsızlıklara neden oldu. Oluşturulan başlatma kodu artık doğru. Ancak, bu değişiklik kaynakta hatalara veya uyarılara neden olabilir ve bu örnekte olduğu gibi yanlışlıkla yanlış sıralanmış üyelere dayanır:

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

Önceki sürümlerde, oluşturucu veri üyesinden önce veri üyesini Innervyanlış başlatacaktı. (C++ standardı, üyelerin bildirim sırasıyla aynı olan bir başlatma sırası gerektirir). Oluşturulan kod artık standarda uygun olduğuna göre member-init-list sıra dışıdır. Derleyici bu örnek için bir uyarı oluşturur. Bunu düzeltmek için member-initializer-list öğesini bildirim sırasını yansıtacak şekilde yeniden sıralayın.

Tamser aşırı yüklemeleri ve long bağımsız değişkenleri içeren aşırı yükleme çözümlemesi

C++ standardı, bir long -dönüştürmenin standart dönüştürme olarak derecesini gerektirir int . Önceki MSVC derleyicileri, aşırı yükleme çözümlemesi için daha yüksek sırada yer alan bir tamser yükseltmesi olarak yanlış derecelendirdi. Bu derecelendirme, belirsiz olarak kabul edilmesi gerektiğinde aşırı yükleme çözümlemenin başarıyla çözülmesine neden olabilir.

Derleyici artık dereceyi modda doğru olarak /permissive- değerlendirir. Bu örnekte olduğu gibi geçersiz kod doğru şekilde tanılanır:

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
}

Bu sorunu çeşitli yollarla düzeltebilirsiniz:

  • Çağrı sitesinde, geçirilen bağımsız değişkenin türünü olarak intdeğiştirin. Değişken türünü değiştirebilir veya yayınlayabilirsiniz.

  • Çok sayıda çağrı sitesi varsa, bağımsız değişken alan başka bir long aşırı yükleme ekleyebilirsiniz. Bu işlevde bağımsız değişkenini yayınlayıp aşırı yüklemeye iletin int .

İç bağlantı ile tanımlanmamış değişkenin kullanımı

MSVC'nin Visual Studio 2019 sürüm 16.7'den önceki sürümleri, iç bağlantısı olduğu bildirilen extern ve tanımlanmayan bir değişkenin kullanımını kabul etti. Bu tür değişkenler başka bir çeviri biriminde tanımlanamaz ve geçerli bir program oluşturamaz. Derleyici şimdi bu durumu derleme zamanında tanılar. Bu hata, tanımlanmamış statik işlevlerin hatasına benzer.

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

Bu program daha önce yanlış derlenmiş ve bağlanmış, ancak şimdi C7631 hatası yayacak.

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

Bu tür değişkenler, kullanıldıkları çeviri biriminde tanımlanmalıdır. Örneğin, açık bir başlatıcı veya ayrı bir tanım sağlayabilirsiniz.

Tür eksiksizliği ve türetilmiş-temel işaretçi dönüştürmeleri

C++20'den önceki C++ standartlarında türetilmiş bir sınıftan temel sınıfa dönüştürme, türetilmiş sınıfın tam bir sınıf türü olmasını gerektirmez. C++ standart komitesi, C++ dilinin tüm sürümleri için geçerli olan bir geriye dönük Hata Raporu değişikliğini onayladı. Bu değişiklik, türetilmiş sınıfın tam bir sınıf türü olmasını gerektiren dönüştürme işlemini gibi std::is_base_oftür özellikleriyle hizalar.

Bir örnek aşağıda verilmiştir:

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

Bu davranış değişikliği yalnızca /std:c++20 veya /std:c++latestiçin değil, MSVC'nin tüm C++ dil modları için geçerlidir.

Daraltma dönüştürmeleri daha tutarlı bir şekilde tanılanmıştır

MSVC, bir braced-list başlatıcısında dönüştürmeleri daraltmaya yönelik bir uyarı yayar. Daha önce, derleyici daha büyük enum temel alınan türlerden daha dar integral türlerine daraltma dönüştürmelerini tanılamıyordu. (Derleyici bunları yanlışlıkla dönüştürme yerine tam sayı yükseltmesi olarak değerlendirdi). Daraltma dönüştürmesi kasıtlı olarak yapılırsa, başlatıcı bağımsız değişkeninde kullanarak static_cast uyarıyı önleyebilirsiniz. Alternatif olarak, daha büyük bir hedef tamsayı türü de seçebilirsiniz.

Uyarıyı ele almak için açık static_cast kullanma örneği aşağıda verilmişti:

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
}

Visual Studio 2019 sürüm 16.8'de uyumluluk geliştirmeleri

'Lvalue olarak kullanılan sınıf rvalue' uzantısı

MSVC,lvalue olarak rvalue sınıfı kullanılmasına izin veren bir uzantıya sahiptir. Uzantı, rvalue sınıfının ömrünü uzatmaz ve çalışma zamanında tanımsız davranışa yol açabilir. Şimdi standart kuralı zorunlu kılıp altında /permissive-bu uzantıya izin vermeyeceğiz. Henüz kullanamıyorsanız /permissive- , uzantıya açıkça izin vermek için kullanabilirsiniz /we4238 . Bir örnek aşağıda verilmiştir:

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

'Ad alanı olmayan kapsamda açık özelleştirme' uzantısı

MSVC'nin ad alanı olmayan kapsamda açık özelleştirmeye izin veren bir uzantısı vardı. Artık CWG 727'nin çözünürlüğünden sonra standardın bir parçasıdır. Ancak davranış farklılıkları vardır. Derleyicimizin davranışını standartla uyumlu olacak şekilde ayarladık.

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

Soyut sınıf türleri denetleniyor

C++20 Standardı, soyut sınıf türünün işlev parametresi olarak kullanımını algılamak için derleyicilerin kullandığı işlem işlemini değiştirdi. Özellikle, bu artık bir SFINAE hatası değildir. Daha önce, derleyici bir işlev şablonunun uzmanlığının bir işlev parametresi olarak soyut sınıf türü örneğine sahip olacağını algılarsa, bu özelleştirmenin geçersiz biçimlendirilmiş olarak kabul edileceğini düşünmektedir. Uygulanabilir aday işlevleri kümesine eklenmez. C++20'de, işlev çağrılana kadar soyut sınıf türünde bir parametre denetimi gerçekleşmez. Bunun etkisi, derlemek için kullanılan kodun hataya neden olmayacağıdır. Bir örnek aşağıda verilmiştir:

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

Daha önce çağrısıcompare, için Tbir String şablon bağımsız değişkeni kullanarak işlev şablonunu compare özelleştirmeye çalışırdı. Soyut bir sınıf olduğundan String geçerli bir uzmanlık oluşturulamaz. Uygun tek aday.compare(const Node&, const Node&) Ancak, C++20 altında işlev çağrılana kadar soyut sınıf türü denetimi gerçekleşmez. Bu nedenle, uzmanlık compare(String, String) uygulanabilir adaylar kümesine eklenir ve en iyi aday olarak seçilir, çünkü 'den const String& 'a Stringconst Node&dönüştürme, dönüştürmeden const String& daha iyi bir dönüştürme dizisidir.

C++20 altında, bu örnekteki olası düzeltmelerden biri kavramları kullanmaktır; öğesinin tanımını compare şu şekilde değiştirin:

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

C++ kavramları kullanılamıyorsa SFINAE'ye geri dönebilirsiniz:

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

P0960R3 desteği - değerlerin parantez içinde bir listesinden toplamaların başlatılmasına izin verme

C++20 P0960R3 , parantez içinde başlatıcı listesi kullanarak toplama başlatma desteği ekler. Örneğin, aşağıdaki kod C++20'de geçerlidir:

struct S {
    int i;
    int j;
};

S s(1, 2);

Bu özelliğin çoğu ek bir özelliktir, yani kod artık daha önce derlenmiş olmayan bir şekilde derleniyor. Ancak, davranışını std::is_constructibledeğiştirir. C++17 modunda bu static_assert başarısız olur, ancak C++20 modunda başarılı olur:

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

Aşırı yükleme çözümlemesinin denetimi için bu tür özelliğini kullanırsanız, C++17 ile C++20 arasında davranış değişikliğine yol açabilir.

İşlev şablonlarıyla ilgili aşırı yükleme çözümlemesi

Daha önce derleyici, altında derlememesi /permissive- gereken bazı kodların derlenemesine izin verdi. Etkisi, derleyicinin çalışma zamanı davranışında değişikliğe yol açan yanlış işlevi çağırmasıydı:

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ğrısı, ve N::folmak üzere g iki işlev ::f içeren bir aşırı yükleme kümesi kullanır. Bir N::f işlev şablonu olduğundan, derleyici işlev bağımsız değişkenini çıkarılmayan bir bağlam olarak ele almalıdır. Bu durumda, g derleyici şablon parametresi Tiçin bir tür çıkaramadiğinden çağrısının başarısız olması gerektiği anlamına gelir. Ne yazık ki, derleyici işlev çağrısı için iyi bir eşleşme olduğuna karar vermiş olduğu ::f gerçeğini atmadı. Derleyici, bir hata yaymak yerine bağımsız değişken olarak kullanarak ::f çağrılacak g kod oluşturur.

İşlev bağımsız değişkeni olarak işlevinin kullanıcının beklediği gibi kullanılması ::f çoğu durumda, yalnızca kod ile /permissive-derlenmişse bir hata yayarız.

C /await ++20 eş yordamlarına geçiş

Standart C++20 eş yordamları artık ve /std:c++latestaltında /std:c++20 varsayılan olarak açıktır. Coroutines TS'den ve seçeneğin altındaki destekten /await farklıdırlar. 'den /await standart yordamlara geçiş için bazı kaynak değişiklikleri gerekebilir.

Standart olmayan anahtar sözcükler

Eski await ve yield anahtar sözcükler C++20 modunda desteklenmez. Bunun yerine kodun co_yield ve kullanması co_await gerekir. Standart mod, bir eş yordamda kullanılmasına return da izin vermez. Bir coroutine içindeki her return öğesini kullanmalıdır co_return.

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

initial_suspend/final_suspend türleri

altında /await, promise initial ve suspend işlevleri döndüren boololarak bildirilebilir. Bu davranış standart değildir. C++20'de bu işlevlerin beklenebilir bir sınıf türü döndürmesi gerekir; genellikle önemsiz beklenebilir türlerden biridir: std::suspend_always işlev daha önce döndürdüyse trueveya std::suspend_never döndürdüyse 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{}; }
    ...
};

Türü yield_value

C++20'de promise yield_value işlevinin beklenebilir bir tür döndürmesi gerekir. modunda /await işlevinin yield_value döndürerek voidher zaman askıya alınmasına izin verilirdi. Bu tür işlevler, döndüren std::suspend_alwaysbir işlevle değiştirilebilir.

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

Özel durum işleme işlevi

/awaitözel durum işleme işlevi olmayan bir promise türünü veya bir alan std::exception_ptradlı set_exception bir özel durum işleme işlevini destekler. C++20'de promise türünün bağımsız değişken içermeyen adlı unhandled_exception bir işlevi olmalıdır. Gerekirse özel durum nesnesi öğesinden std::current_exception alınabilir.

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

Eş yordamların çıkarılmış dönüş türleri desteklenmiyor

C++20, gibi autobir yer tutucu türü içeren dönüş türüne sahip eş yordamları desteklemez. Dönüş türü olan coroutine'ler açıkça bildirilmelidir. altında /await, bu çıkarılmış türler her zaman deneysel bir tür içerir ve gerekli türü tanımlayan bir üst bilgi eklenmesini gerektirir: biri std::experimental::task<T>, std::experimental::generator<T>veya 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;
};

Dönüş türü return_value

Promise return_value işlevinin dönüş türü olmalıdır void. Modda /await , dönüş türü herhangi bir şey olabilir ve yoksayılır. Bu tanılama, yazarın dönüş değerinin return_value bir arayana döndürülmesi gibi küçük hataları algılamaya yardımcı olabilir.

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

Dönüş nesnesi dönüştürme davranışı

Bir coroutine'un bildirilen dönüş türü promise get_return_object işlevinin dönüş türüyle eşleşmiyorsa, öğesinden get_return_object döndürülen nesne, coroutine dönüş türüne dönüştürülür. altında /await, bu dönüştürme, coroutine gövdesinin yürütme şansına sahip olmadan önce yapılır. veya /std:c++latestiçinde /std:c++20 bu dönüştürme, değer çağırana döndürülürken gerçekleştirilir. İlk askıya alma noktasında askıya alınmayan eş yordamların, coroutine gövdesi içinde tarafından get_return_object döndürülen nesnenin kullanılmasını sağlar.

Coroutine promise parametreleri

C++20'de derleyici, coroutine parametrelerini (varsa) promise türünün oluşturucusunda geçirmeye çalışır. Başarısız olursa, varsayılan bir oluşturucuyla yeniden denenir. Modda /await , yalnızca varsayılan oluşturucu kullanıldı. Bu değişiklik, sözde birden çok oluşturucu varsa davranışta bir farka yol açabilir. Ya da bir coroutine parametresinden promise türüne dönüştürme varsa.

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- ve C++20 Modülleri varsayılan olarak /std:c++20

C++20 Modülleri desteği ve /std:c++latestaltında /std:c++20 varsayılan olarak açıktır. Bu değişiklik ve koşullu olarak anahtar sözcük olarak ele alınan moduleimport senaryolar hakkında daha fazla bilgi için bkz . Visual Studio 2019 sürüm 16.8'de MSVC ile Standart C++20 Modülleri desteği.

Modül desteğinin önkoşulu olarak artık permissive- veya /std:c++latest belirtildiğinde /std:c++20 etkinleştirilir. Daha fazla bilgi için bkz. /permissive-.

Altında daha önce derlenen /std:c++latest ve uyumlu olmayan derleyici davranışları gerektiren kod için, /permissive derleyicide katı uyumluluk modunu kapatmak için belirtilebilir. Derleyici seçeneği, komut satırı bağımsız değişken listesinden sonra /std:c++latest görünmelidir. Ancak Modül /permissive kullanımı algılanırsa hatayla sonuçlanır:

hata C1214: Modüller 'option' aracılığıyla istenen standart dışı davranışla çakışıyor

Seçenek için en yaygın değerler şunlardır:

Seçenek Açıklama
/Zc:twoPhase- C++20 Modülleri için iki aşamalı ad araması gereklidir ve tarafından /permissive-örtülür.
/Zc:hiddenFriend- Standart gizli arkadaş adı arama kuralları C++20 Modülleri için gereklidir ve tarafından /permissive-örtülür.
/Zc:lambda- Standart lambda işleme C++20 Modülleri için gereklidir ve mod veya üzeri tarafından /std:c++20 örtülür.
/Zc:preprocessor- Uyumlu ön işlemci yalnızca C++20 üst bilgi birimi kullanımı ve oluşturma için gereklidir. Adlandırılmış Modüller bu seçeneği gerektirmez.

Henüz /experimental:module standartlaştırılmadığından Visual Studio ile birlikte gelen Modülleri kullanmak std.* için bu seçenek hala gereklidir.

seçeneği/experimental:module, , /Zc:lambdave /Zc:hiddenFriendifadelerini de ifade eder/Zc:twoPhase. Daha önce Modüller ile derlenen kod bazen Modül yalnızca tüketildiyse ile /Zc:twoPhase- derlenebilirdi. Bu davranış artık desteklenmiyor.

Visual Studio 2019 sürüm 16.9'da uyumluluk geliştirmeleri

Doğrudan başlatma başvurusunda geçici olarak kopya başlatma

Çekirdek Çalışma Grubu sorunu CWG 2267 , ayraçlı başlatıcı listesi ile kümeli başlatıcı listesi arasındaki tutarsızlıkla ilgilendi. Çözünürlük, iki formu uyumlu hale alır.

Visual Studio 2019 sürüm 16.9, değiştirilen davranışı tüm /std derleyici modlarında uygular. Ancak, kaynakta hataya neden olabilecek bir değişiklik olduğundan, yalnızca kod kullanılarak /permissive-derlendiğinde desteklenir.

Bu örnekte davranış değişikliği gösterilmektedir:

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
}

Yıkıcı özellikleri ve potansiyel olarak yapılandırılmış alt nesneler

Temel Çalışma Grubu sorunu CWG 2336 , sanal temel sınıflara sahip sınıflardaki yıkıcıların örtük özel durum belirtimleriyle ilgili bir eksiklik içerir. Eksiklik, türetilmiş bir sınıftaki bir yıkıcının temel sınıftan daha zayıf bir özel durum belirtimine sahip olabileceği anlamına geliyordu. Bu temel soyutsa ve bir virtual temele sahipse.

Visual Studio 2019 sürüm 16.9, değiştirilen davranışı tüm /std derleyici modlarında uygular.

Bu örnek, yorumun nasıl değiştiğini gösterir:

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

Bu değişiklik öncesinde, yalnızca potansiyel olarak yapılandırılmış alt nesneler dikkate alındığından için B örtük olarak tanımlanmış yıkıcısı idi noexcept. Temel sınıfV, bir temel ve B soyut olduğundan, potansiyel olarak oluşturulmuş bir virtual alt nesne değildir. Ancak, temel sınıf V , sınıfının olası olarak oluşturulmuş bir alt nesnesidir Dve bu nedenle D::~D , temelinden daha zayıf bir özel durum belirtimine sahip türetilmiş bir sınıfa yol açan olarak belirlenir noexcept(false). Bu yorum güvenli değil. B'den türetilen bir sınıfın yıkıcısından bir özel durum oluşursa yanlış çalışma zamanı davranışına yol açabilir.

Bu değişiklikle, bir yıkıcının sanal yıkıcısı varsa ve herhangi bir sanal temel sınıfın potansiyel olarak bir yıkıcı oluşturması varsa da oluşturma olasılığı vardır.

Benzer türler ve başvuru bağlaması

Temel Çalışma Grubu sorunu CWG 2352 , başvuru bağlama kuralları ile tür benzerliği değişiklikleri arasındaki tutarsızlıkla ilgilenir. Tutarsızlık önceki Hata Raporlarında (CWG 330 gibi ) sunulmuştur. Bu, Visual Studio 2019'un 16.0 ile 16.8 sürümlerini etkiledi.

Bu değişiklikle, Visual Studio 2019 sürüm 16.9'dan başlayarak, daha önce Visual Studio 2019 sürüm 16.0 ile 16.8 arasındaki geçici bir başvuruya bağlı olan kod, yalnızca cv-niteleyicilerle farklılık gösterdiğinde doğrudan bağlanabilir.

Visual Studio 2019 sürüm 16.9, değiştirilen davranışı tüm /std derleyici modlarında uygular. Bu potansiyel olarak kaynakta hataya neden olan bir değişikliktir.

İlgili bir değişiklik için bkz . Eşleşmeyen cv-niteleyicileri olan tür başvuruları .

Bu örnek, değiştirilen davranışı gösterir:

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
}

Güncelleştirme, kullanıma sunulan geçici bir uygulamaya dayalı program davranışını değiştirebilir:

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 ve /Zc:twoPhase- seçenek davranışı değişikliği

Normalde, MSVC derleyici seçenekleri en son görülenin kazandığı ilke üzerinde çalışır. Ne yazık ki ve /Zc:twoPhase- seçeneklerinde durum /Zc:twoPhase böyle değildi. Bu seçenekler "yapışkan" olduğundan, sonraki seçenekler bunları geçersiz kılamaz. Örneğin:

cl /Zc:twoPhase /permissive a.cpp

Bu durumda, ilk /Zc:twoPhase seçenek katı iki aşamalı ad aramasını etkinleştirir. İkinci seçenek, katı uyumluluk modunu devre dışı bırakmak içindir (tam tersidir /permissive-), ancak devre dışı bırakılmadı /Zc:twoPhase.

Visual Studio 2019 sürüm 16.9, tüm /std derleyici modlarında bu davranışı değiştirir. /Zc:twoPhase ve /Zc:twoPhase- artık "yapışkan" değildir ve daha sonraki seçenekler bunları geçersiz kılabilir.

Yıkıcı şablonlarında açık noexcept-tanımlayıcıları

Derleyici daha önce, oluşturmayan özel durum belirtimi ile bildirilen ancak açık bir noexcept-tanımlayıcısı olmadan tanımlanan bir yıkıcı şablonunu kabul etti. Bir yıkıcının örtük özel durum belirtimi, sınıfın özelliklerine bağlıdır - bir şablonun tanımı noktasında tanınmayan özellikler. C++ Standardı da bu davranışı gerektirir: Bir yıkıcı noexcept-specifier olmadan bildirilirse, örtük bir özel durum belirtimine sahiptir ve işlevin başka hiçbir bildiriminde noexcept-specifier olmayabilir.

Visual Studio 2019 sürüm 16.9, tüm /std derleyici modlarında uyumlu davranışa dönüşür.

Bu örnek, derleyici davranışındaki değişikliği gösterir:

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

C++20'de yeniden yazılan ifadeler

Visual Studio 2019 sürüm 16.2'den bu yana, altında /std:c++latestderleyici aşağıdaki örneğe benzer bir kod kabul etti:

#include <compare>

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

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

Ancak derleyici, yazarın bekleyebilecek karşılaştırma işlevini çağırmaz. Yukarıdaki kod olarak (a <=> b) < 0yeniden yazılmalıdıra < b. Bunun yerine, derleyici kullanıcı tanımlı dönüştürme işlevini kullandı operator bool() ve karşılaştırmıştı bool(a) < bool(b). Visual Studio 2019 sürüm 16.9 ve sonraki sürümlerinde, derleyici beklenen uzay gemisi işleç ifadesini kullanarak ifadeyi yeniden yazar.

Kaynak hataya neden olan değişiklik

Yeniden yazılan ifadelere dönüştürmelerin düzgün uygulanması başka bir etkiye sahiptir: Derleyici, ifadeyi yeniden yazma girişimlerinden kaynaklanan belirsizlikleri de doğru bir şekilde tanılar. Bu örneği ele alalım:

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

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

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

C++17'de, ifadenin sağ tarafında türetilmiş tabana dönüştürmesi Derived nedeniyle bu kod kabul edilir. C++20'de, sentezlenen ifade adayı da eklenir: Derived{} == Base{}. Standartta hangi işlevin dönüştürmelere göre kazandığına ilişkin kurallar nedeniyle ile arasındaki Base::operator==Derived::operator== seçimin kararsız olduğu ortaya çıkar. İki ifadedeki dönüştürme dizileri birbirinden iyi veya kötü olmadığından, örnek kod belirsizliğe neden olur.

Belirsizliği çözmek için iki dönüştürme dizisine tabi olmayacak yeni bir aday ekleyin:

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

Çalışma zamanı hataya neden olan değişiklik

C++20'deki işleç yeniden yazma kuralları nedeniyle, aşırı yükleme çözümlemesi, aksi takdirde daha düşük bir dil modunda bulmayacağı yeni bir aday bulmak için mümkündür. Yeni aday, eski adaydan daha iyi bir eşleşme olabilir. Bu örneği ele alalım:

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

C++17'de için tek aday ci == *this olur const_iterator::operator==. Türetilmiş bir tabana dönüştürme const_iteratorişleminden geçtiğinden bu bir eşleşmedir*this. C++20'de başka bir yeniden yazılan aday eklenir: *this == ciöğesini çağıran iterator::operator==. Bu aday dönüştürme gerektirmez, bu nedenle değerinden const_iterator::operator==daha iyi bir eşleşmedir. Yeni adayla ilgili sorun şu anda tanımlanan işlev olmasıdır, bu nedenle işlevin yeni semantiği sonsuz özyinelemeli tanımına iterator::operator==neden olur.

Örnek gibi kodlara yardımcı olmak için derleyici yeni bir uyarı uygular:

$ 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

Kodu düzeltmek için hangi dönüştürmenin kullanılacağı konusunda açık olun:

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

Visual Studio 2019 sürüm 16.10'da uyumluluk geliştirmeleri

Sınıfın kopya başlatması için yanlış aşırı yükleme seçildi

Bu örnek kod göz önüne alındığında:

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

Derleyicinin önceki sürümleri, öğesinin şablonlu dönüştürme oluşturucusunu kullanarak bağımsız değişkenini f türünden CAöğesine A yanlış dönüştürürdü. Standart C++, bunun yerine dönüştürme işlecinin B::operator A kullanılmasını gerektirir. Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde, aşırı yükleme çözümleme davranışı doğru aşırı yüklemeyi kullanacak şekilde değiştirilir.

Bu değişiklik, seçilen aşırı yüklemeyi başka bazı durumlarda da düzeltebilir:

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

Kayan nokta değişmez değerlerinin yanlış ayrıştırılması

Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde kayan nokta değişmez değerleri, gerçek türlerine göre ayrıştırılır. Derleyicinin önceki sürümleri her zaman bir kayan nokta değişmez değeri türü double varmış gibi ayrıştırdı ve ardından sonucu gerçek türe dönüştürdü. Bu davranış, geçerli değerlerin yanlış yuvarlanmasına ve reddedilmesine neden olabilir:

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

Yanlış bildirim noktası

Derleyicinin önceki sürümleri kendi kendine başvuran kodu şu örnekteki gibi derleyemiyordu:

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

    int value() const;
};

S s(4, &s);

Derleyici, oluşturucu bağımsız değişkenleri de dahil olmak üzere bildirimin tamamını ayrıştırana kadar değişkeni s bildirmez. Oluşturucu bağımsız değişken listesinde öğesinin araması s başarısız olur. Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerde bu örnek artık doğru şekilde derlenmiş.

Ne yazık ki bu değişiklik, bu örnekte olduğu gibi mevcut kodu bozabilir:

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

Derleyicinin önceki sürümlerinde, oluşturucu bağımsız değişkenlerinde "inner" bildirimine sbaktığındas, önceki bildirimi ("dış" s) bulur ve kod derler. Sürüm 16.10'dan başlayarak derleyici bunun yerine C4700 uyarısını yayar. Bunun nedeni, derleyicinin oluşturucu bağımsız değişkenlerini ayrıştırmadan önce "iç" s değerini bildirmesidir. Bu nedenle arama, s henüz başlatılmamış olan "inner" söğesini bulur.

Sınıf şablonunun açıkça özelleştirilmiş üyesi

Derleyicinin önceki sürümleri, sınıf şablonu üyesinin açık bir özelleştirmesini, birincil şablonda da tanımlanmış gibi inline yanlış olarak işaretledi. Bu davranış, derleyicinin bazen uyumlu kodu reddedeceği anlamına geliyordu. Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde, açık bir özelleştirme artık örtük olarak modda /permissive- olarak inline işaretlenmez. Bu örneği ele alalım:

Kaynak dosya s.h:

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

Kaynak dosya s.cpp:

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

Kaynak dosya main.cpp:

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

int main()
{
}

Yukarıdaki örnekteki bağlayıcı hatasını gidermek için öğesine açıkça S<int>::fekleyininline:

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

Çıkarılmış dönüş türü adı mangling

Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde, derleyici dönüş türlerinin neden olduğu işlevler için karışık adlar oluşturma şeklini değiştirdi. Örneğin, şu işlevleri göz önünde bulundurun:

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

Derleyicinin önceki sürümleri bağlayıcı için şu adları oluşturur:

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

Şaşırtıcı bir şekilde, işlev gövdesindeki yerel lambda'nın g neden olduğu diğer semantik davranışlar nedeniyle dönüş türü atlanabilir. Bu tutarsızlık, çıkarılmış dönüş türüne sahip dışarı aktarılan işlevlerin uygulanmasını zorlaştırdı: Modül arabirimi, işlevin gövdesinin nasıl derlendiği hakkında bilgi gerektirir. İçeri aktarma tarafında tanıma düzgün bir şekilde bağlanabilen bir işlev üretmek için bilgilere ihtiyaç duyar.

Derleyici artık çıkarılmış dönüş türü işlevinin dönüş türünü atlar. Bu davranış, diğer önemli uygulamalarla tutarlıdır. İşlev şablonları için bir özel durum vardır: Derleyicinin bu sürümü, çıkarılmış dönüş türüne sahip işlev şablonları için yeni bir mangled-name davranışı ekler:

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;

ve için autodecltype(auto) mangled adları artık ikilide görünür, çıkarılmış dönüş türünde görünmez:

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)

Derleyicinin önceki sürümleri imzanın bir parçası olarak çıkarılmış dönüş türünü içerecekti. Derleyici, mangled adına dönüş türünü eklediğinde bağlayıcı sorunlarına neden olabilir. Aksi takdirde iyi biçimlendirilmiş bazı senaryolar bağlayıcı için belirsiz hale gelir.

Yeni derleyici davranışı ikili hataya neden olan bir değişiklik üretebilir. Bu örneği ele alalım:

Kaynak dosya a.cpp:

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

Kaynak dosya main.cpp:

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

16.10 sürümünden önceki sürümlerde, derleyici, farklı işlevler olsalar bile gibi int f()görünen bir ad auto f() üretmişti. Bu, örneğin derlenecek olduğu anlamına gelir. Sorunu düzeltmek için özgün tanımına fgüvenmeyinauto. Bunun yerine olarak int f()yazın. Çıkış dönüş türlerine sahip işlevler her zaman derlendiğinden, ABI etkileri en aza indirilir.

Yoksayılan nodiscard öznitelik için uyarı

Derleyicinin önceki sürümleri bir nodiscard özniteliğin belirli kullanımlarını sessizce yoksayacaktı. Bildirilmekte olan işlev veya sınıf için geçerli olmayan söz dizimsel bir konumdaysa özniteliğini yoksayarlar. Örneğin:

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

Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde, derleyici bunun yerine düzey 4 uyarısı C5240 yayar:

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

Bu sorunu düzeltmek için özniteliğini doğru söz dizimsel konuma taşıyın:

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

Modül purview'da sistem üst bilgi adlarına sahip yönergeler için include uyarı

Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde, derleyici ortak modül arabirimi yazma hatasını önlemek için bir uyarı yayar. Bir deyimden sonra export module standart kitaplık üst bilgisi eklerseniz, derleyici C5244 uyarısını yayar. Bir örnek aşağıda verilmiştir:

export module m;
#include <vector>

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

Geliştirici büyük olasılıkla modülünün m içeriğine sahip olmayı amaçlamamış olabilir <vector>. Derleyici şimdi sorunu bulup düzeltmeye yardımcı olmak için bir uyarı yayar:

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

Bu sorunu düzeltmek için öncesinde export module m;hareket edin#include <vector>:

#include <vector>
export module m;

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

Kullanılmayan iç bağlantı işlevleri için uyarı

Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde derleyici, iç bağlantı içeren başvurulmayan bir işlevin kaldırıldığı daha fazla durumda uyarır. Derleyicinin önceki sürümleri aşağıdaki kod için C4505 uyarısını yayardı:

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

Derleyici artık anonim ad alanları içindeki başvurulmayan auto işlevler ve başvurulmayan işlevler hakkında da uyarır. Aşağıdaki işlevlerin her ikisi için de varsayılan olmayan bir uyarı C5245 yayar:

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

Küme ayracı elision'da uyarı

Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde, derleyici alt nesneler için küme ayracı kullanmayan başlatma listelerinde uyarır. Derleyici varsayılan olarak kapalı uyarı C5246 yayar.

Bir örnek aşağıda verilmiştir:

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

Bu sorunu düzeltmek için alt nesnenin başlatılmasını küme ayraçları içinde sarmala:

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

Bir const nesnenin başlatılmamış olup olmadığını doğru algılama

Visual Studio 2019 sürüm 16.10 ve sonraki sürümlerinde, tam olarak başlatılmamış bir const nesne tanımlamaya çalıştığınızda derleyici artık C2737 hatasını yayar:

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

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

Derleyicinin önceki sürümleri, başlatılmamış olsa S::i bile bu kodun derlemesine izin verdi.

Bu sorunu çözmek için, bir nesnenin örneğini oluşturmadan const önce tüm üyeleri başlatın:

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

Visual Studio 2019 sürüm 16.11'de uyumluluk geliştirmeleri

/std:c++20 derleyici modu

Visual Studio 2019 sürüm 16.11 ve sonraki sürümlerde, derleyici artık derleyici modunu destekliyor /std:c++20 . Daha önce C++20 özellikleri visual studio 2019'da /std:c++latest yalnızca modda kullanılabilirdi. Başlangıçta gerekli /std:c++latest olan C++20 özellikleri artık Visual Studio'nun /std:c++20 en son sürümlerinde modda veya daha sonraki sürümlerde çalışıyor.

Ayrıca bkz.

Microsoft C/C++ dil uyumluluğu