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++latest
altı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++latest
altı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_difference
set_intersection
, is_heap
ve is_heap_until
'nin is_sorted
yeni 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::decay
std::decay_t
).
Özellik testi makroları
P0941R2 - özellik testi makroları , desteğiyle __has_cpp_attribute
tamamlandı. Ö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_cast
derleyici 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 /ZW
altı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 bool
kullanı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 /Wall
yü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 /Wall
gö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++latest
altı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 a
ekleyin: 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_type
ve std::allocator::difference_type
artık kullanım dışı değildir.
Dize dönüştürmelerini daraltma için doğru uyarı
static_cast
std::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, temelCreateDirectoryExW
alınan işlev bir symlink olduğundaexisting_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_relative
artık LWG 3096 olarak bildirilen sondaki eğik çizgiyle karıştırılmamaktadır.- içinde
std::filesystem::create_symlink
eğ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_searcher
std::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
std
doğru şekilde bildirirtimespec
timespec_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 gibiprivate
işaretlemek için daha fazla düzeltme.,
map
veunordered_map
gibilist
düğüm tabanlı kapsayıcıların bozulmasına neden olan özel durum güvenliği doğruluğu sorunlarını giderdik. Birpropagate_on_container_copy_assignment
veyapropagate_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_swap
değerlerinepropagate_on_container_copy_assignment
gö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
\r\n
işleme =>\n
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=
gerileme
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>::type
const T
bir 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. u8
C++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()
veends_with()
basic_string_view
ve içinbasic_string
.- İlişkili kapsayıcılar için
contains()
. list
veforward_list
içinremove()
,remove_if()
veunique()
artıksize_type
değerini döndürüyor.shift_left()
veshift_right()
algoritmaya <>eklendi.
16.2'de uyumluluk iyileştirmeleri
noexcept
constexpr
Işlev
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ği
char8_t
- P0600R1:
[[nodiscard]]
STL için, Bölüm 1 - P0653R2:
to_address()
- P0754R2: <sürüm>
- P0771R1:
noexcept
std::function
's move oluşturucu için
İlişkili kapsayıcılar için en iyi karşılaştırıcılar
, , map
multiset
ve multimap
içindeki set
arama 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 const
yapı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 f
de 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 false
kullanı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 int
verilmiş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 long
verilir.
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 int
olduğ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:preprocessor
kullanı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_t
std::ostream
ekleme ve char16_t
ekleme veya veya std::wostream
char32_t
std::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++latest
altı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_t
akış 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_t
kullanı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 float
ile ç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
olarakdouble
değiştirin. Bu bağımsız değişken, tarafındanpow
döndürülen türle eşleşecek şekilde doğrudan öğesinecomplex<double>
dönüştürülebilir.ifadesini söyleyerek
complex<float>{pow(ARG, ARG)}
sonucunupow
olarakcomplex<float>
daraltın. Daha sonra birfloat
değerle çarpmaya devam edebilirsiniz.yerine
int
öğesine geçirin.pow
float
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 enum
ve 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++17
C7626 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++latest
gibi /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 /w45220
derlerseniz 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 bool
dö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 nullptr
ekleyin 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_t
yalnı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 false
değ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_list
oluş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 Inner
v
yanlış 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
int
değ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 iletinint
.
İç 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_of
tü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++latest
iç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 T
bir 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 String
const 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_constructible
değ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::f
olmak ü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 T
iç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++latest
altı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 bool
olarak 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 true
veya 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 void
her zaman askıya alınmasına izin verilirdi. Bu tür işlevler, döndüren std::suspend_always
bir 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_ptr
adlı 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 auto
bir 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++latest
iç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++latest
altı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 module
import
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:lambda
ve /Zc:hiddenFriend
ifadelerini 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 D
ve 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++latest
derleyici 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) < 0
yeniden 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_iterator
iş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 C
A
öğ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 s
baktığı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>::f
ekleyininline
:
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 auto
decltype(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 f
gü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.