Peningkatan C++ Conformance, perubahan perilaku, dan perbaikan bug di Visual Studio 2017
Microsoft C/C++ di Visual Studio (MSVC) melakukan peningkatan kesesuaian dan perbaikan bug di setiap rilis. Artikel ini mencantumkan peningkatan menurut rilis utama, lalu menurut versi. Untuk langsung beralih ke perubahan untuk versi tertentu, gunakan daftar di bawah Dalam artikel ini.
Dokumen ini mencantumkan perubahan di Visual Studio 2017. Untuk panduan perubahan di Visual Studio 2022, lihat Peningkatan kesesuaian C++ di Visual Studio 2022. Untuk panduan tentang perubahan di Visual Studio 2019, lihat Peningkatan kesesuaian C++ di Visual Studio 2019. Untuk daftar lengkap peningkatan kesesuaian sebelumnya, lihat Visual C++ Yang Baru 2003 hingga 2015.
Peningkatan kesesuaian di Visual Studio 2017 RTW (versi 15.0)
Dengan dukungan untuk generalisasi constexpr
dan inisialisasi anggota data nonstatis (NSDMI) untuk agregat, kompiler MSVC di Visual Studio 2017 sekarang lengkap untuk fitur yang ditambahkan dalam standar C++14. Namun, pengompilasi masih kekurangan beberapa fitur dari standar C++11 dan C++98. Lihat kesesuaian bahasa Microsoft C/C++ untuk mengetahui status pengompilasi saat ini.
C++11: Ekspresi dukungan SFINAE di lebih banyak pustaka
Pengompilasi terus meningkatkan dukungannya untuk ekspresi SFINAE. Ini diperlukan untuk pengurangan dan penggantian argumen template di mana ekspresi decltype
dan constexpr
mungkin muncul sebagai parameter template. Untuk informasi selengkapnya, lihat Peningkatan ekspresi SFINAE di Visual Studio RC 2017.
C++14: NSDMI untuk agregat
Agregat adalah array atau kelas yang memiliki: tidak ada konstruktor yang disediakan pengguna, tidak ada anggota data nonstatis yang privat atau dilindungi, tidak ada kelas dasar, dan tidak ada fungsi virtual. Mulai dari C++14, agregat mungkin berisi inisialisasi anggota. Untuk informasi selengkapnya, lihat Inisialisasi anggota dan agregat.
C++14: Diperpanjang constexpr
Ekspresi yang dinyatakan sebagai constexpr
sekarang diizinkan untuk berisi jenis deklarasi tertentu, jika dan beralih pernyataan, pernyataan perulangan, dan mutasi objek yang masa pakainya dimulai dalam evaluasi ekspresi constexpr
. Tidak ada lagi persyaratan bahwa fungsi anggota nonstatis constexpr
harus secara implisit const
. Untuk informasi lebih lanjut, lihat Melonggarkan batasan pada fungsi constexpr
.
C++17: Terse static_assert
parameter pesan untuk static_assert
bersifat opsional. Untuk informasi selengkapnya, lihat N3928: Memperluas static_assert, v2.
C++17: atribut [[fallthrough]]
Dalam mode /std:c++17
dan yang lebih baru, atribut [[fallthrough]]
dapat digunakan dalam konteks pernyataan pengalihan sebagai petunjuk untuk pengompilasi bahwa perilaku fall-through dimaksudkan. Atribut ini mencegah pengompilasi mengeluarkan peringatan dalam kasus seperti itu. Untuk informasi selengkapnya, lihat P0188R0 - Wording for [[fallthrough]] attribute
.
Loop for
berbasis rentang umum
Loop berbasis rentang for
tidak lagi memerlukannya begin()
dan end()
mengembalikan objek dengan jenis yang sama. Perubahan ini memungkinkan end()
untuk mengembalikan sentinel seperti yang digunakan oleh rentang di range-v3
dan Spesifikasi Teknis Rentang yang diselesaikan tetapi tidak cukup diterbitkan. Untuk informasi selengkapnya, lihat P0184R0 - Generalizing the Range-Based for Loop
.
Salin-daftar-inisialisasi
Visual Studio 2017 dengan benar menimbulkan kesalahan pengompilasi yang terkait dengan pembuatan objek menggunakan daftar penginisialisasi. Kesalahan ini tidak tertangkap pada Visual Studio 2015, dan dapat menyebabkan crash atau perilaku runtime yang tidak terdefinisi. Sesuai N4594 13.3.1.7p1
, dalam copy-list-initialization
, pengompilasi diperlukan untuk mempertimbangkan konstruktor eksplisit untuk resolusi kelebihan beban. Namun, itu harus memunculkan kesalahan jika kelebihan beban tertentu dipilih.
Dua contoh berikut dikompilasi pada Visual Studio 2015 tetapi tidak pada Visual Studio 2017.
struct A
{
explicit A(int) {}
A(double) {}
};
int main()
{
A a1 = { 1 }; // error C3445: copy-list-initialization of 'A' cannot use an explicit constructor
const A& a2 = { 1 }; // error C2440: 'initializing': cannot convert from 'int' to 'const A &'
}
Untuk memperbaiki kesalahan, gunakan inisialisasi langsung:
A a1{ 1 };
const A& a2{ 1 };
Pada Visual Studio 2015, kompilator secara keliru memperlakukan copy-list-initialization dengan cara yang sama seperti inisialisasi salinan reguler: dianggap hanya mengonversi konstruktor untuk resolusi kelebihan beban. Dalam contoh berikut, Visual Studio 2015 memilih MyInt(23)
. Visual Studio 2017 memunculkan kesalahan dengan benar.
// From http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1228
struct MyStore {
explicit MyStore(int initialCapacity);
};
struct MyInt {
MyInt(int i);
};
struct Printer {
void operator()(MyStore const& s);
void operator()(MyInt const& i);
};
void f() {
Printer p;
p({ 23 }); // C3066: there are multiple ways that an object
// of this type can be called with these arguments
}
Contoh ini mirip dengan yang sebelumnya tetapi menimbulkan kesalahan yang berbeda. Ini berhasil pada Visual Studio 2015 dan gagal pada Visual Studio 2017 dengan C2668.
struct A {
explicit A(int) {}
};
struct B {
B(int) {}
};
void f(const A&) {}
void f(const B&) {}
int main()
{
f({ 1 }); // error C2668: 'f': ambiguous call to overloaded function
}
Typedef yang tidak digunakan lagi
Visual Studio 2017 sekarang mengeluarkan peringatan yang benar untuk typedef usang yang dideklarasikan di kelas atau struct. Contoh berikut mengompilasi tanpa peringatan pada Visual Studio 2015. Ini menghasilkan C4996 pada Visual Studio 2017.
struct A
{
// also for __declspec(deprecated)
[[deprecated]] typedef int inttype;
};
int main()
{
A::inttype a = 0; // C4996 'A::inttype': was declared deprecated
}
constexpr
Visual Studio 2017 dengan benar memunculkan kesalahan saat operand kiri dari operasi evaluasi bersyarat tidak valid dalam konteks constexpr
. Kode berikut dikompilasi di Visual Studio 2015, tetapi tidak di Visual Studio 2017, yang memunculkan C3615:
template<int N>
struct array
{
int size() const { return N; }
};
constexpr bool f(const array<1> &arr)
{
return arr.size() == 10 || arr.size() == 11; // C3615 constexpr function 'f' cannot result in a constant expression
}
Untuk memperbaiki kesalahan, nyatakan fungsi array::size()
sebagai constexpr
atau hapus kualifikasi constexpr
dari f
.
Jenis kelas diteruskan ke fungsi variadik
Di Visual Studio 2017, kelas atau struct yang diteruskan ke fungsi variadik seperti printf
harus dapat disalin secara mudah. Ketika objek tersebut diteruskan, pengompilasi hanya membuat salinan bitwise dan tidak memanggil konstruktor atau destruktor.
#include <atomic>
#include <memory>
#include <stdio.h>
int main()
{
std::atomic<int> i(0);
printf("%i\n", i); // error C4839: non-standard use of class 'std::atomic<int>'
// as an argument to a variadic function.
// note: the constructor and destructor will not be called;
// a bitwise copy of the class will be passed as the argument
// error C2280: 'std::atomic<int>::atomic(const std::atomic<int> &)':
// attempting to reference a deleted function
struct S {
S(int i) : i(i) {}
S(const S& other) : i(other.i) {}
operator int() { return i; }
private:
int i;
} s(0);
printf("%i\n", s); // warning C4840 : non-portable use of class 'main::S'
// as an argument to a variadic function
}
Untuk memperbaiki kesalahan, Anda dapat memanggil fungsi anggota yang mengembalikan tipe yang dapat disalin secara mudah,
std::atomic<int> i(0);
printf("%i\n", i.load());
atau gunakan transmisi statis untuk mengonversi objek sebelum meneruskannya:
struct S {/* as before */} s(0);
printf("%i\n", static_cast<int>(s))
Untuk string yang dibangun dan dikelola menggunakan CString
, operator LPCTSTR()
yang disediakan harus digunakan untuk mentransmisikan objek CString
ke penunjuk C yang diharapkan oleh string format.
CString str1;
CString str2 = _T("hello!");
str1.Format(_T("%s"), static_cast<LPCTSTR>(str2));
Kualifikasi CV dalam konstruksi kelas
Pada Visual Studio 2015, pengompilasi terkadang salah mengabaikan kualifikasi cv saat membuat objek kelas melalui panggilan konstruktor. Masalah ini berpotensi menyebabkan crash atau perilaku runtime yang tidak terduga. Contoh berikut mengompilasi di Visual Studio 2015 tetapi menimbulkan kesalahan pengompilasi di Visual Studio 2017:
struct S
{
S(int);
operator int();
};
int i = (const S)0; // error C2440
Untuk memperbaiki kesalahan, nyatakan operator int()
sebagai const
.
Pemeriksaan akses pada nama yang memenuhi syarat dalam template
Versi pengompilasi sebelumnya tidak memeriksa akses ke nama yang memenuhi syarat dalam beberapa konteks template. Masalah ini dapat mengganggu perilaku SFINAE yang diharapkan, di mana penggantian diperkirakan gagal karena tidak dapat diaksesnya nama. Hal ini berpotensi menyebabkan crash atau perilaku tak terduga saat runtime, karena pengompilasi salah menyebut kelebihan beban operator yang salah. Pada Visual Studio 2017, kesalahan pengompilasi dimunculkan. Kesalahan spesifik mungkin bervariasi, tetapi kesalahan umumnya adalah C2672, "tidak ditemukan fungsi kelebihan beban yang cocok." Kode berikut dikompilasi di Visual Studio 2015 tetapi menimbulkan kesalahan di Visual Studio 2017:
#include <type_traits>
template <class T> class S {
typedef typename T type;
};
template <class T, std::enable_if<std::is_integral<typename S<T>::type>::value, T> * = 0>
bool f(T x);
int main()
{
f(10); // C2672: No matching overloaded function found.
}
Daftar argumen template hilang
Pada Visual Studio 2015 dan yang lebih lama, pengompilasi tidak mendiagnosis semua daftar argumen template yang hilang. Ini tidak akan dicatat ketika template yang hilang muncul dalam daftar parameter templat: misalnya, ketika bagian dari argumen templat default atau parameter templat nonjenis hilang. Masalah ini dapat mengakibatkan perilaku yang tidak dapat diprediksi, termasuk crash pengompilasi atau perilaku runtime yang tidak terduga. Kode berikut dikompilasi pada Visual Studio 2015, tetapi menghasilkan kesalahan dalam Visual Studio 2017.
template <class T> class ListNode;
template <class T> using ListNodeMember = ListNode<T> T::*;
template <class T, ListNodeMember M> class ListHead; // C2955: 'ListNodeMember': use of alias
// template requires template argument list
// correct: template <class T, ListNodeMember<T> M> class ListHead;
Expression-SFINAE
Untuk mendukung ekspresi-SFINAE, pengompilasi sekarang mengurai argumen decltype
ketika template dideklarasikan daripada instans. Jadi, jika spesialisasi nondependen ditemukan dalam argumen decltype
, argumen ini tidak ditangguhkan ke instantiation-time. Ini segera diproses, dan kesalahan yang dihasilkan didiagnosis pada saat itu.
Contoh berikut menunjukkan kesalahan pengompilasi yang dimunculkan pada titik deklarasi:
#include <utility>
template <class T, class ReturnT, class... ArgsT>
class IsCallable
{
public:
struct BadType {};
template <class U>
static decltype(std::declval<T>()(std::declval<ArgsT>()...)) Test(int); //C2064. Should be declval<U>
template <class U>
static BadType Test(...);
static constexpr bool value = std::is_convertible<decltype(Test<T>(0)), ReturnT>::value;
};
constexpr bool test1 = IsCallable<int(), int>::value;
static_assert(test1, "PASS1");
constexpr bool test2 = !IsCallable<int*, int>::value;
static_assert(test2, "PASS2");
Kelas dideklarasikan dalam namespace anonim
Menurut standar C++, kelas yang dinyatakan di dalam namespace anonim memiliki tautan internal, dan itu berarti tidak dapat diekspor. Pada Visual Studio 2015 dan yang lebih lama, aturan ini tidak diberlakukan. Pada Visual Studio 2017, aturan diberlakukan sebagian. Pada Visual Studio 2017 contoh berikut menimbulkan kesalahan C2201:
struct __declspec(dllexport) S1 { virtual void f() {} };
// C2201 const anonymous namespace::S1::vftable: must have external linkage
// in order to be exported/imported.
Inisialisasi default untuk anggota kelas nilai (C++/CLI)
Pada Visual Studio 2015 dan yang lebih lama, pengompilasi mengizinkan (tetapi mengabaikan) inisialisasi anggota default untuk anggota kelas nilai. Inisialisasi default kelas nilai selalu menginisialisasi anggota secara nol. Konstruktor default tidak diizinkan. Pada Visual Studio 2017, inisialisasi anggota default menimbulkan kesalahan pengompilasi, seperti yang ditunjukkan dalam contoh ini:
value struct V
{
int i = 0; // error C3446: 'V::i': a default member initializer
// isn't allowed for a member of a value class
};
Pengindeks default (C++/CLI)
Pada Visual Studio 2015 dan yang lebih lama, pengompilasi dalam beberapa kasus salah mengidentifikasi properti default sebagai pengindeks default. Dimungkinkan untuk mengatasi masalah dengan menggunakan pengidentifikasi default
untuk mengakses properti. Solusinya sendiri menjadi bermasalah setelah default
diperkenalkan sebagai kata kunci di C++11. Pada Visual Studio 2017, bug yang memerlukan solusi telah diperbaiki. Pengompilasi sekarang menimbulkan kesalahan ketika default
digunakan untuk mengakses properti default untuk kelas.
//class1.cs
using System.Reflection;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
[DefaultMember("Value")]
public class Class1
{
public int Value
{
// using attribute on the return type triggers the compiler bug
[return: MarshalAs(UnmanagedType.I4)]
get;
}
}
[DefaultMember("Value")]
public class Class2
{
public int Value
{
get;
}
}
}
// code.cpp
#using "class1.dll"
void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
r1->Value; // error
r1->default;
r2->Value;
r2->default; // error
}
Pada Visual Studio 2017, Anda dapat mengakses kedua properti Nilai berdasarkan namanya:
#using "class1.dll"
void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
r1->Value;
r2->Value;
}
Peningkatan kesesuaian di 15.3
constexpr
lambda
Ekspresi Lambda sekarang dapat digunakan dalam ekspresi konstanta. Untuk informasi selengkapnya, lihat constexpr
ekspresi lambda di C++.
if constexpr
dalam template fungsi
Template fungsi mungkin berisi pernyataan if constexpr
untuk mengaktifkan percabangan waktu kompilasi. Untuk informasi selengkapnya, lihat if constexpr
pernyataan.
Pernyataan pilihan dengan penginisialisasi
Pernyataan if
dapat mencakup penginisialisasi yang memperkenalkan variabel pada cakupan blok dalam pernyataan itu sendiri. Untuk informasi lebih lanjut, lihat if
pernyataan dengan penginisialisasi.
[[maybe_unused]]
dan atribut [[nodiscard]]
Atribut baru [[maybe_unused]]
menghentikan peringatan saat entitas tidak digunakan. Atribut [[nodiscard]]
membuat peringatan jika nilai pengembalian panggilan fungsi dibuang. Untuk informasi selengkapnya, lihat Atribut di C++.
Menggunakan namespace atribut tanpa pengulangan
Sintaks baru untuk mengaktifkan hanya pengidentifikasi namespace layanan tunggal dalam daftar atribut. Untuk informasi selengkapnya, lihat Atribut di C++.
Pengikatan terstruktur
Sekarang dimungkinkan dalam satu deklarasi untuk menyimpan nilai dengan nama individual untuk komponennya, ketika nilainya adalah array, atau std::tuple
std::pair
, atau memiliki semua anggota data nonstatis publik. Untuk informasi selengkapnya, lihat P0144R0 - Structured Bindings
dan Mengembalikan beberapa nilai dari suatu fungsi.
Aturan konstruksi untuk nilai enum class
Sekarang ada konversi implisit untuk enumerasi tercakup yang tidak menyempit. Ini mengonversi dari jenis enumerasi terlingkup yang mendasar menjadi enumerasi itu sendiri. Konversi tersedia ketika definisinya tidak memperkenalkan enumerator, dan ketika sumber menggunakan sintaksis inisialisasi daftar. Untuk informasi selengkapnya, lihat P0138R2 - Construction Rules for enum class Values
dan Enumerasi.
Menangkap *this
berdasarkan nilai
Objek *this
dalam ekspresi lambda sekarang dapat ditangkap oleh nilai. Perubahan ini memungkinkan skenario di mana lambda dipanggil dalam operasi paralel dan asinkron, terutama pada arsitektur mesin yang lebih baru. Untuk informasi selengkapnya, lihat P0018R3 - Lambda Capture of *this by Value as [=,*this]
.
Menghapus operator++
untuk bool
operator++
tidak lagi didukung pada jenis bool
. Untuk informasi selengkapnya, lihat P0002R1 - Remove Deprecated operator++(bool)
.
Menghapus kata kunci register
yang tidak digunakan lagi
Kata kunci register
, yang sebelumnya tidak digunakan lagi (dan diabaikan oleh pengompilasi), sekarang dihapus dari bahasa. Untuk informasi selengkapnya, lihat P0001R1 - Remove Deprecated Use of the register Keyword
.
Panggilan ke template anggota yang dihapus
Dalam versi Visual Studio sebelumnya, pengompilasi dalam beberapa kasus akan gagal memancarkan kesalahan untuk panggilan yang tidak terbentuk ke template anggota yang dihapus. Panggilan ini berpotensi menyebabkan crash pada waktu proses. Kode berikut sekarang menghasilkan C2280:
template<typename T>
struct S {
template<typename U> static int f() = delete;
};
void g()
{
decltype(S<int>::f<int>()) i; // this should fail with
// C2280: 'int S<int>::f<int>(void)': attempting to reference a deleted function
}
Untuk memperbaiki kesalahan, nyatakan i
sebagai int
.
Pemeriksaan prakondisi untuk sifat tipe
Visual Studio 2017 versi 15.3 meningkatkan pemeriksaan prakondisi untuk sifat jenis agar lebih ketat mengikuti standar. Salah satu pemeriksaan tersebut adalah untuk dapat ditetapkan. Kode berikut menghasilkan C2139 di Visual Studio 2017 versi 15.3:
struct S;
enum E;
static_assert(!__is_assignable(S, S), "fail"); // C2139 in 15.3
static_assert(__is_convertible_to(E, E), "fail"); // C2139 in 15.3
Peringatan pengompilasi baru dan pemeriksaan runtime pada marshaling native ke terkelola
Memanggil dari fungsi terkelola ke fungsi native membutuhkan marshaling. CLR melakukan marshaling, tetapi tidak memahami semantik C++. Jika Anda meneruskan objek native berdasarkan nilai, CLR memanggil copy-constructor objek atau menggunakan BitBlt
, yang dapat menyebabkan perilaku yang tidak terdefinisi saat runtime.
Sekarang pengompilasi memancarkan peringatan jika menemukan kesalahan ini pada waktu kompilasi: objek asli dengan ctor salinan yang dihapus akan diteruskan antara batas asli dan terkelola berdasarkan nilai. Untuk kasus-kasus di mana kompilator tidak tahu pada waktu kompilasi, itu menyuntikkan pemeriksaan runtime sehingga program memanggil std::terminate
segera ketika terjadi kesalahan susunan. Pada Visual Studio 2017 versi 15.3, kode berikut menghasilkan peringatan C4606:
class A
{
public:
A() : p_(new int) {}
~A() { delete p_; }
A(A const &) = delete;
A(A &&rhs) {
p_ = rhs.p_;
}
private:
int *p_;
};
#pragma unmanaged
void f(A a)
{
}
#pragma managed
int main()
{
// This call from managed to native requires marshaling. The CLR doesn't
// understand C++ and uses BitBlt, which results in a double-free later.
f(A()); // C4606 'A': passing argument by value across native and managed
// boundary requires valid copy constructor. Otherwise, the runtime
// behavior is undefined.`
}
Untuk memperbaiki kesalahan, hapus direktif #pragma managed
untuk menandai pemanggil sebagai native dan hindari marshaling.
Peringatan API eksperimental untuk WinRT
API WinRT yang dirilis untuk eksperimen dan umpan balik didekorasi dengan Windows.Foundation.Metadata.ExperimentalAttribute
. Dalam Visual Studio 2017 versi 15.3, pengompilasi menghasilkan peringatan C4698 untuk atribut ini. Beberapa API dalam versi SDK Windows sebelumnya telah didekorasi dengan atribut, dan panggilan ke API ini sekarang memicu peringatan pengompilasi ini. SDK Windows yang lebih baru memiliki atribut yang dihapus dari semua jenis yang dikirim. Jika Anda menggunakan SDK yang lebih lama, Anda harus menekan peringatan ini untuk semua panggilan ke jenis yang dikirim.
Kode berikut menghasilkan peringatan C4698:
Windows::Storage::IApplicationDataStatics2::GetForUserAsync(); // C4698
// 'Windows::Storage::IApplicationDataStatics2::GetForUserAsync' is for
// evaluation purposes only and is subject to change or removal in future updates
Untuk menonaktifkan peringatan, tambahkan #pragma:
#pragma warning(push)
#pragma warning(disable:4698)
Windows::Storage::IApplicationDataStatics2::GetForUserAsync();
#pragma warning(pop)
Definisi di luar baris dari fungsi anggota template
Visual Studio 2017 versi 15.3 menghasilkan kesalahan untuk definisi di luar baris dari fungsi anggota template yang tidak dideklarasikan di kelas. Kode berikut sekarang menghasilkan kesalahan C2039:
struct S {};
template <typename T>
void S::f(T t) {} // C2039: 'f': is not a member of 'S'
Untuk memperbaiki kesalahan, tambahkan deklarasi ke kelas :
struct S {
template <typename T>
void f(T t);
};
template <typename T>
void S::f(T t) {}
Mencoba mengambil alamat pointer this
Dalam C++, this
adalah nilai dari penunjuk tipe ke X. Anda tidak dapat mengambil alamat this
atau mengikatnya ke referensi nilai. Dalam versi Visual Studio sebelumnya, pengompilasi akan memungkinkan Anda untuk menghindari pembatasan ini dengan menggunakan transmisi. Dalam Visual Studio 2017 versi 15.3, pengompilasi menghasilkan kesalahan C2664.
Konversi ke kelas dasar yang tidak dapat diakses
Visual Studio 2017 versi 15.3 menghasilkan kesalahan saat Anda mencoba mengonversi jenis ke kelas dasar yang tidak dapat diakses. Kode berikut tidak terbentuk dan berpotensi menyebabkan crash saat runtime. Pengompilasi sekarang menghasilkan C2243 ketika melihat kode seperti ini:
#include <memory>
class B { };
class D : B { }; // C2243: 'type cast': conversion from 'D *' to 'B *' exists, but is inaccessible
void f()
{
std::unique_ptr<B>(new D());
}
Argumen default tidak diperbolehkan pada definisi fungsi anggota di luar baris
Argumen default tidak diperbolehkan pada definisi fungsi anggota di luar baris di kelas template. Pengompilasi akan mengeluarkan peringatan di bawah /permissive
, dan kesalahan keras di bawah /permissive-
.
Dalam versi Visual Studio sebelumnya, kode yang tidak terbentuk berikut berpotensi menyebabkan crash runtime. Visual Studio 2017 versi 15.3 menghasilkan peringatan C5037:
template <typename T>
struct A {
T f(T t, bool b = false);
};
template <typename T>
T A<T>::f(T t, bool b = false) // C5037: 'A<T>::f': an out-of-line definition of a member of a class template cannot have default arguments
{
// ...
}
Untuk memperbaiki kesalahan, hapus argumen = false
default.
Penggunaan offsetof
dengan penanda anggota majemuk
Di Visual Studio 2017 versi 15.3, menggunakan offsetof(T, m)
di mana m adalah "penunjuk anggota gabungan" menghasilkan peringatan saat Anda mengompilasi dengan opsi /Wall
. Kode berikut tidak terbentuk dan berpotensi menyebabkan crash saat runtime. Visual Studio 2017 versi 15.3 menghasilkan peringatan C4841:
struct A {
int arr[10];
};
// warning C4841: non-standard extension used: compound member designator used in offsetof
constexpr auto off = offsetof(A, arr[2]);
Untuk memperbaiki kode, nonaktifkan peringatan dengan pragma atau ubah kode agar tidak menggunakan offsetof
:
#pragma warning(push)
#pragma warning(disable: 4841)
constexpr auto off = offsetof(A, arr[2]);
#pragma warning(pop)
Menggunakan offsetof
dengan anggota data statis atau fungsi anggota
Dalam Visual Studio 2017 versi 15.3, menggunakan offsetof(T, m)
di mana m mengacu pada anggota data statis atau fungsi anggota menghasilkan kesalahan. Kode berikut menghasilkan kesalahan C4597:
#include <cstddef>
struct A {
int ten() { return 10; }
static constexpr int two = 2;
};
constexpr auto off = offsetof(A, ten); // C4597: undefined behavior: offsetof applied to member function 'A::ten'
constexpr auto off2 = offsetof(A, two); // C4597: undefined behavior: offsetof applied to static data member 'A::two'
Kode ini tidak terbentuk dan berpotensi menyebabkan crash pada runtime. Untuk memperbaiki kesalahan, ubah kode menjadi tidak lagi memanggil perilaku yang tidak ditentukan. Ini adalah kode nonportabel yang tidak diizinkan oleh standar C++.
Peringatan baru pada atribut __declspec
Di Visual Studio 2017 versi 15.3, kompiler tidak lagi mengabaikan atribut jika __declspec(...)
diterapkan sebelum extern "C"
spesifikasi tautan. Sebelumnya, pengompilasi akan mengabaikan atribut, yang dapat memiliki implikasi runtime. /Wall
Ketika opsi dan /WX
diatur, kode berikut menghasilkan peringatan C4768:
__declspec(noinline) extern "C" HRESULT __stdcall // C4768: __declspec attributes before linkage specification are ignored
Untuk memperbaiki peringatan, letakkan extern "C"
terlebih dahulu:
extern "C" __declspec(noinline) HRESULT __stdcall
Peringatan ini nonaktif secara default di Visual Studio 2017 versi 15.3, dan hanya memengaruhi kode yang dikompilasi dengan /Wall
/WX
. Mulai Visual Studio 2017 versi 15.5, ini diaktifkan secara default sebagai peringatan tingkat 3.
decltype
dan panggilan ke destruktor yang dihapus
Di versi Visual Studio sebelumnya, pengompilasi tidak mendeteksi saat panggilan ke destruktor yang dihapus terjadi dalam konteks ekspresi yang terkait dengan decltype
. Dalam Visual Studio 2017 versi 15.3, kode berikut menghasilkan kesalahan C2280:
template<typename T>
struct A
{
~A() = delete;
};
template<typename T>
auto f() -> A<T>;
template<typename T>
auto g(T) -> decltype((f<T>()));
void h()
{
g(42); // C2280: 'A<T>::~A(void)': attempting to reference a deleted function
}
Variabel const yang tidak diinisialisasi
Visual Studio rilis RTW 2017 memiliki regresi: pengompilasi C++ tidak akan mengeluarkan diagnostik untuk variabel const
yang tidak diinisialisasi. Regresi ini telah diperbaiki di Visual Studio 2017 versi 15.3. Kode berikut sekarang menghasilkan peringatan C4132:
const int Value; // C4132: 'Value': const object should be initialized
Untuk memperbaiki kesalahan, tetapkan nilai ke Value
.
Deklarasi kosong
Visual Studio 2017 versi 15.3 sekarang memperingatkan pada deklarasi kosong untuk semua jenis, bukan hanya jenis bawaan. Kode berikut sekarang menghasilkan peringatan C4091 tingkat 2 untuk keempat deklarasi:
struct A {};
template <typename> struct B {};
enum C { c1, c2, c3 };
int; // warning C4091 : '' : ignored on left of 'int' when no variable is declared
A; // warning C4091 : '' : ignored on left of 'main::A' when no variable is declared
B<int>; // warning C4091 : '' : ignored on left of 'B<int>' when no variable is declared
C; // warning C4091 : '' : ignored on left of 'C' when no variable is declared
Untuk menghapus peringatan, beri komentar atau hapus deklarasi kosong. Dalam kasus di mana objek yang tidak disebutkan namanya dimaksudkan untuk memiliki efek samping (seperti RAII), objek harus diberi nama.
Peringatan dikecualikan di bawah /Wv:18
dan aktif secara default di bawah tingkat peringatan W2.
std::is_convertible
untuk jenis array
Versi pengompilasi sebelumnya memberikan hasil yang salah untuk jenis array std::is_convertible
. Ini mengharuskan penulis perpustakaan untuk membuat kasus khusus pada pengompilasi Microsoft C++ saat menggunakan sifat tipe std::is_convertible<...>
. Dalam contoh berikut, pernyataan statis meneruskan versi Visual Studio sebelumnya tetapi gagal di Visual Studio 2017 versi 15.3:
#include <type_traits>
using Array = char[1];
static_assert(std::is_convertible<Array, Array>::value);
static_assert(std::is_convertible<const Array, const Array>::value, "");
static_assert(std::is_convertible<Array&, Array>::value, "");
static_assert(std::is_convertible<Array, Array&>::value, "");
std::is_convertible<From, To>
dihitung dengan memeriksa untuk melihat apakah definisi fungsi imajiner terbentuk dengan baik:
To test() { return std::declval<From>(); }
Destruktor privat dan std::is_constructible
Versi pengompilasi sebelumnya mengabaikan apakah destruktor bersifat privat saat memutuskan hasil std::is_constructible
. Versi ini sekarang mempertimbangkannya. Dalam contoh berikut, pernyataan statis meneruskan versi Visual Studio sebelumnya tetapi gagal di Visual Studio 2017 versi 15.3:
#include <type_traits>
class PrivateDtor {
PrivateDtor(int) { }
private:
~PrivateDtor() { }
};
// This assertion used to succeed. It now correctly fails.
static_assert(std::is_constructible<PrivateDtor, int>::value);
Destruktor privat menyebabkan jenis menjadi tidak dapat dibangun. std::is_constructible<T, Args...>
dihitung seolah-olah deklarasi berikut ditulis:
T obj(std::declval<Args>()...)
Panggilan ini menyiratkan panggilan destruktor.
C2668: Resolusi kelebihan beban ambigu
Versi pengompilasi sebelumnya terkadang gagal mendeteksi ambiguitas ketika menemukan beberapa kandidat melalui menggunakan deklarasi dan pencarian yang bergantung pada argumen. Kegagalan ini dapat menyebabkan kelebihan beban yang salah dipilih, dan perilaku runtime yang tidak terduga. Dalam contoh berikut, Visual Studio 2017 versi 15.3 dengan benar meningkatkan C2668:
namespace N {
template<class T>
void f(T&, T&);
template<class T>
void f();
}
template<class T>
void f(T&, T&);
struct S {};
void f()
{
using N::f;
S s1, s2;
f(s1, s2); // C2668: 'f': ambiguous call to overloaded function
}
Untuk memperbaiki kode, hapus pernyataan penggunaan N::f
jika Anda berniat untuk memanggil ::f()
.
C2660: deklarasi fungsi lokal dan pencarian tergantung argumen
Deklarasi fungsi lokal menyembunyikan deklarasi fungsi dalam cakupan penutup dan menonaktifkan pencarian yang bergantung pada argumen. Versi pengompilasi sebelumnya selalu melakukan pencarian yang bergantung pada argumen dalam kasus ini. Ini berpotensi menyebabkan perilaku runtime yang tidak terduga, jika pengompilasi memilih kelebihan beban yang salah. Biasanya, kesalahan ini karena tanda tangan yang salah dari deklarasi fungsi lokal. Dalam contoh berikut, Visual Studio 2017 versi 15.3 dengan benar meningkatkan C2660:
struct S {};
void f(S, int);
void g()
{
void f(S); // C2660 'f': function does not take 2 arguments:
// or void f(S, int);
S s;
f(s, 0);
}
Untuk memperbaiki masalah, ubah tanda tangan f(S)
atau hapus.
C5038: urutan inisialisasi dalam daftar penginisialisasi
Anggota kelas diinisialisasi dalam urutan yang dideklarasikan, bukan urutan mereka muncul dalam daftar penginisialisasi. Versi pengompilasi sebelumnya tidak memperingatkan ketika urutan daftar penginisialisasi berbeda dari urutan deklarasi. Masalah ini dapat menyebabkan perilaku runtime yang tidak terdefinisi jika inisialisasi satu anggota bergantung pada anggota lain dalam daftar yang sudah diinisialisasi. Dalam contoh berikut, Visual Studio 2017 versi 15.3 (dengan /Wall
) meningkatkan peringatan C5038:
struct A
{ // Initialized in reverse, y reused
A(int a) : y(a), x(y) {} // C5038: data member 'A::y' will be initialized after data member 'A::x'
int x;
int y;
};
Untuk memperbaiki masalah, atur daftar penginisialisasi agar memiliki urutan yang sama dengan deklarasi. Peringatan serupa dimunculkan ketika satu atau kedua penginisialisasi merujuk ke anggota kelas dasar.
Peringatan ini tidak default, dan hanya memengaruhi kode yang dikompilasi dengan /Wall
.
Penyempurnaan kesesuaian di 15.5
Fitur yang ditandai dengan [14] tersedia tanpa syarat bahkan dalam mode /std:c++14
.
Pengubah pengompilasi baru untuk extern constexpr
Dalam versi Visual Studio sebelumnya, pengompilasi selalu memberikan constexpr
tautan internal variabel, bahkan ketika variabel ditandai extern
. Dalam Visual Studio 2017 versi 15.5, pengubah pengompilasi baru, /Zc:externConstexpr
, memungkinkan perilaku yang benar dan sesuai standar. Untuk informasi selengkapnya, lihat extern constexpr
perintah.
Menghapus spesifikasi pengecualian dinamis
P0003R5 Spesifikasi pengecualian dinamis tidak digunakan lagi di C++11. Fitur ini dihapus dari C++17, tetapi spesifikasi throw()
yang (masih) tidak digunakan lagi disimpan secara ketat sebagai alias untuk noexcept(true)
. Untuk informasi selengkapnya, lihat Penghapusan spesifikasi pengecualian dinamis dan noexcept
.
not_fn()
P0005R4not_fn
adalah pengganti dan not1
not2
.
Kata sandi ulang enable_shared_from_this
P0033R1enable_shared_from_this
ditambahkan di C++11. Standar C++17 memperbarui spesifikasi untuk menangani kasus sudut tertentu dengan lebih baik. [14]
Menyambung peta dan set
P0083R3 Fitur ini memungkinkan ekstraksi simpul dari kontainer asosiatif (yaitu, map
, set
, unordered_map
, unordered_set
) yang kemudian dapat dimodifikasi dan dimasukkan kembali ke kontainer yang sama atau ckyang sama. (Kasus penggunaan umum adalah mengekstrak simpul dari std::map
, mengubah kunci, dan memasukkan ulang.)
Menghentikan penggunaan bagian pustaka vestigial
P0174R2 Beberapa fitur pustaka standar C++ telah digantikan oleh fitur yang lebih baru selama bertahun-tahun, atau ditemukan tidak berguna, atau bermasalah. Fitur-fitur ini secara resmi tidak digunakan lagi di C++17.
Menghapus dukungan pengalokasi di std::function
P0302R1 Sebelum C++17, template kelas std::function
memiliki beberapa konstruktor yang mengambil argumen pengalokasi. Namun, penggunaan alokator dalam konteks ini bermasalah, dan semantiknya tidak jelas. Konstruktor masalah telah dihapus.
Perbaikan untuk not_fn()
P0358R1 Kata-kata baru untuk std::not_fn
memberikan dukungan penyebaran kategori nilai saat digunakan dalam pemanggilan pembungkus.
shared_ptr<T[]>
, shared_ptr<T[N]>
P0414R2 Penggabungan shared_ptr
berubah dari Library Fundamentals ke C++17. [14]
Memperbaiki array shared_ptr
P0497R0 Perbaikan untuk shared_ptr dukungan untuk array. [14]
Menjelaskan insert_return_type
P0508R0 Kontainer asosiatif dengan kunci unik, dan kontainer tidak berurutan dengan kunci unik memiliki fungsi anggota insert
yang mengembalikan tipe bersarang insert_return_type
. Jenis pengembalian tersebut sekarang didefinisikan sebagai spesialisasi jenis yang diparameterkan pada Iterator dan NodeType kontainer.
Variabel sebaris untuk pustaka standar
Untuk P0607R0, beberapa variabel umum yang dideklarasikan dalam pustaka standar sekarang dinyatakan sebaris.
Fitur Annex D tidak digunakan lagi
Lampiran D standar C++ berisi semua fitur yang tidak digunakan lagi, termasuk shared_ptr::unique()
, <codecvt>
, dan namespace std::tr1
. Ketika opsi pengompilasi /std:c++17
atau yang lebih baru disetel, hampir semua fitur pustaka standar di Lampiran D ditandai sebagai tidak digunakan lagi. Untuk informasi selengkapnya, lihat Fitur pustaka standar di Annex D ditandai sebagai tidak digunakan lagi.
Namespace std::tr2::sys
di <experimental/filesystem>
sekarang mengeluarkan peringatan penghentian di bawah /std:c++14
secara default, dan sekarang dihapus di bawah /std:c++17
dan yang lebih baru secara default.
Kesesuaian yang ditingkatkan di <iostream>
dengan menghindari ekstensi nonstandar (spesialisasi eksplisit dalam kelas).
Pustaka standar sekarang menggunakan template variabel secara internal.
Pustaka standar diperbarui sebagai respons terhadap perubahan pengkompilasi C++17. Pembaruan mencakup penambahan noexcept
dalam sistem tipe, dan penghapusan spesifikasi pengecualian dinamis.
Perubahan pengurutan parsial
Pengompilasi sekarang menolak kode berikut dengan benar dan memberikan pesan kesalahan yang benar:
template<typename... T>
int f(T* ...)
{
return 1;
}
template<typename T>
int f(const T&)
{
return 2;
}
int main()
{
int i = 0;
f(&i); // C2668
}
t161.cpp
t161.cpp(16): error C2668: 'f': ambiguous call to overloaded function
t161.cpp(8): note: could be 'int f<int*>(const T &)'
with
[
T=int*
]
t161.cpp(2): note: or 'int f<int>(int*)'
t161.cpp(16): note: while trying to match the argument list '(int*)'
Masalah dalam contoh di atas adalah bahwa ada dua perbedaan dalam jenis (const vs. non-const dan pack vs. non-pack). Untuk menghilangkan kesalahan pengompilasi, hapus salah satu perbedaan. Kemudian pengompilasi dapat secara tidak ambigu memesan fungsi.
template<typename... T>
int f(T* ...)
{
return 1;
}
template<typename T>
int f(T&)
{
return 2;
}
int main()
{
int i = 0;
f(&i);
}
Pengatur pengecualian
Pengatur referensi ke array atau jenis fungsi tidak pernah cocok untuk objek pengecualian apa pun. Pengompilasi sekarang dengan benar menghormati aturan ini dan meningkatkan peringatan tingkat 4, C4843. Ini juga tidak lagi cocok dengan pengatur char*
atau wchar_t*
dengan string literal saat /Zc:strictStrings
digunakan.
int main()
{
try {
throw "";
}
catch (int (&)[1]) {} // C4843 (This should always be dead code.)
catch (void (&)()) {} // C4843 (This should always be dead code.)
catch (char*) {} // This should not be a match under /Zc:strictStrings
}
warning C4843: 'int (&)[1]': An exception handler of reference to array or function type is unreachable, use 'int*' instead
warning C4843: 'void (__cdecl &)(void)': An exception handler of reference to array or function type is unreachable, use 'void (__cdecl*)(void)' instead
Kode berikut menghindari kesalahan:
catch (int (*)[1]) {}
std::tr1
namespace tidak digunakan lagi
Namespace std::tr1
nonstandar sekarang ditandai sebagai tidak digunakan lagi dalam mode C++14 dan C++17. Pada Visual Studio 2017 versi 15.5, kode berikut menaikkan C4996:
#include <functional>
#include <iostream>
using namespace std;
int main() {
std::tr1::function<int (int, int)> f = std::plus<int>(); //C4996
cout << f(3, 5) << std::endl;
f = std::multiplies<int>();
cout << f(3, 5) << std::endl;
}
warning C4996: 'std::tr1': warning STL4002: The non-standard std::tr1 namespace and TR1-only machinery are deprecated and will be REMOVED. You can define _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING to acknowledge that you have received this warning.
Untuk memperbaiki kesalahan, hapus referensi ke tr1
namespace:
#include <functional>
#include <iostream>
using namespace std;
int main() {
std::function<int (int, int)> f = std::plus<int>();
cout << f(3, 5) << std::endl;
f = std::multiplies<int>();
cout << f(3, 5) << std::endl;
}
Fitur pustaka standar di Annex D ditandai sebagai tidak digunakan lagi
Saat mode /std:c++17
atau pengubah pengompilasi yang lebih baru disetel, hampir semua fitur pustaka standar di Lampiran D ditandai sebagai tidak digunakan lagi.
Pada Visual Studio 2017 versi 15.5, kode berikut menaikkan C4996:
#include <iterator>
class MyIter : public std::iterator<std::random_access_iterator_tag, int> {
public:
// ... other members ...
};
#include <type_traits>
static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");
warning C4996: 'std::iterator<std::random_access_iterator_tag,int,ptrdiff_t,_Ty*,_Ty &>::pointer': warning STL4015: The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. (The <iterator> header is NOT deprecated.) The C++ standard has never required user-defined iterators to derive from std::iterator. To fix this warning, stop deriving from std::iterator and start providing publicly accessible typedefs named iterator_category, value_type, difference_type, pointer, and reference. Note that value_type is required to be non-const, even for constant iterators. You can define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning.
Untuk memperbaiki kesalahan, ikuti instruksi dalam teks peringatan, seperti yang ditunjukkan dalam kode berikut:
#include <iterator>
class MyIter {
public:
typedef std::random_access_iterator_tag iterator_category;
typedef int value_type;
typedef ptrdiff_t difference_type;
typedef int* pointer;
typedef int& reference;
// ... other members ...
};
#include <type_traits>
static_assert(std::is_same<MyIter::pointer, int*>::value, "BOOM");
Variabel lokal yang tidak direferensikan
Dalam Visual Studio 15.5, peringatan C4189 dipancarkan dalam lebih banyak kasus, seperti yang ditunjukkan dalam kode berikut:
void f() {
char s[2] = {0}; // C4189. Either use the variable or remove it.
}
warning C4189: 's': local variable is initialized but not referenced
Untuk memperbaiki kesalahan, hapus variabel yang tidak digunakan.
Komentar satu baris
Dalam Visual Studio 2017 versi 15.5, peringatan C4001 dan C4179 tidak lagi dipancarkan oleh pengompilasi C. Sebelumnya, mereka hanya dipancarkan di bawah pengubah pengompilasi /Za
. Peringatan tidak lagi diperlukan karena komentar satu baris telah menjadi bagian dari standar C sejak C99.
/* C only */
#pragma warning(disable:4001) // C4619
#pragma warning(disable:4179)
// single line comment
//* single line comment */
warning C4619: #pragma warning: there is no warning number '4001'
Ketika kode tidak perlu kompatibel mundur, hindari peringatan dengan menghapus supresi C4001 dan C4179. Jika kode memang perlu kompatibel mundur, tekan C4619 saja.
/* C only */
#pragma warning(disable:4619)
#pragma warning(disable:4001)
#pragma warning(disable:4179)
// single line comment
/* single line comment */
__declspec
atribut dengan tautan extern "C"
Dalam versi Visual Studio sebelumnya, pengompilasi mengabaikan atribut __declspec(...)
saat __declspec(...)
diterapkan sebelum spesifikasi tautan extern "C"
. Perilaku ini menyebabkan kode dihasilkan yang tidak diinginkan pengguna, dengan kemungkinan implikasi runtime. Peringatan C4768 ditambahkan di Visual Studio versi 15.3, tetapi tidak aktif secara default. Di Visual Studio 2017 versi 15.5, peringatan diaktifkan secara default.
__declspec(noinline) extern "C" HRESULT __stdcall // C4768
warning C4768: __declspec attributes before linkage specification are ignored
Untuk memperbaiki kesalahan, letakkan spesifikasi tautan sebelum atribut __declspec:
extern "C" __declspec(noinline) HRESULT __stdcall
Peringatan baru C4768 ini diberikan pada beberapa header SDK Windows yang dikirim dengan Visual Studio 2017 15.3 atau yang lebih lama (misalnya: versi 10.0.15063.0, juga dikenal sebagai RS2 SDK). Namun, versi header SDK Windows yang lebih baru (khususnya, ShlObj.h dan ShlObj_core.h) telah diperbaiki sehingga tidak menghasilkan peringatan. Saat Anda melihat peringatan ini berasal dari header SDK Windows, Anda dapat mengambil tindakan ini:
Beralih ke SDK Windows terbaru yang disertakan dengan rilis Visual Studio 2017 versi 15.5.
Nonaktifkan peringatan di sekitar #include pernyataan header SDK Windows:
#pragma warning (push)
#pragma warning(disable:4768)
#include <shlobj.h>
#pragma warning (pop)
extern constexpr
hubungan
Dalam versi Visual Studio sebelumnya, pengompilasi selalu memberikan constexpr
tautan internal variabel bahkan ketika variabel ditandai extern
. Dalam Visual Studio 2017 versi 15.5, pengubah pengompilasi baru (/Zc:externConstexpr
) memungkinkan perilaku yang benar dan sesuai dengan standar. Akhirnya perilaku ini akan menjadi default.
extern constexpr int x = 10;
error LNK2005: "int const x" already defined
Jika file header berisi variabel yang dideklarasikan extern constexpr
, itu perlu ditandai __declspec(selectany)
agar deklarasi duplikatnya digabungkan dengan benar:
extern constexpr __declspec(selectany) int x = 10;
typeid
tidak dapat digunakan pada jenis kelas yang tidak lengkap
Dalam versi Visual Studio yang lebih lama, pengompilasi salah mengizinkan kode berikut, yang mengakibatkan informasi jenis yang berpotensi salah. Dalam Visual Studio 2017 versi 15.5, pengompilasi dengan benar menimbulkan kesalahan:
#include <typeinfo>
struct S;
void f() { typeid(S); } //C2027 in 15.5
error C2027: use of undefined type 'S'
std::is_convertible
Tipe target
std::is_convertible
mengharuskan jenis target menjadi jenis pengembalian yang valid. Dalam versi Visual Studio sebelumnya, pengompilasi salah mengizinkan jenis abstrak, yang mungkin menyebabkan resolusi kelebihan beban yang salah dan perilaku runtime yang tidak diinginkan. Kode berikut sekarang meningkatkan C2338 dengan benar:
#include <type_traits>
struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };
static_assert(std::is_convertible<D, B>::value, "fail"); // C2338 in 15.5
Untuk menghindari kesalahan, saat menggunakan is_convertible
Anda harus membandingkan jenis penunjuk karena perbandingan jenis bukan penunjuk mungkin gagal jika satu jenis abstrak:
#include <type_traits>
struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };
static_assert(std::is_convertible<D *, B *>::value, "fail");
Penghapusan spesifikasi pengecualian dinamis dan noexcept
Dalam C++17, throw()
adalah alias untuk noexcept
, throw(<type list>)
dan throw(...)
dihapus, dan jenis tertentu mungkin termasuk noexcept
. Perubahan ini dapat menyebabkan masalah kompatibilitas sumber dengan kode yang sesuai dengan C++14 atau yang lebih lama. Beralih /Zc:noexceptTypes-
dapat digunakan untuk kembali ke versi noexcept
C++14 saat menggunakan mode C++17 secara umum. Ini memungkinkan Anda memperbarui kode sumber agar sesuai dengan C++17 tanpa harus menulis ulang semua kode throw()
Anda secara bersamaan.
Pengompilasi juga sekarang mendiagnosis spesifikasi pengecualian yang lebih tidak cocok dalam deklarasi dalam mode C++17 atau dengan /permissive-
peringatan baru C5043.
Kode berikut menghasilkan C5043 dan C5040 di Visual Studio 2017 versi 15.5 saat pengubah /std:c++17
diterapkan:
void f() throw(); // equivalent to void f() noexcept;
void f() {} // warning C5043
void g() throw(); // warning C5040
struct A {
virtual void f() throw();
};
struct B : A {
virtual void f() { } // error C2694
};
Untuk menghapus kesalahan saat masih menggunakan /std:c++17
, tambahkan peralihab /Zc:noexceptTypes-
ke baris perintah, atau perbarui kode Anda untuk menggunakan noexcept
, seperti yang ditunjukkan pada contoh berikut:
void f() noexcept;
void f() noexcept { }
void g() noexcept(false);
struct A {
virtual void f() noexcept;
};
struct B : A {
virtual void f() noexcept { }
};
Variabel sebaris
Anggota data constexpr
statis sekarang secara implisit inline
, yang berarti bahwa deklarasi mereka di dalam kelas sekarang menjadi definisi mereka. Menggunakan definisi di luar baris untuk anggota data static constexpr
berlebihan, dan sekarang tidak digunakan lagi. Di Visual Studio 2017 versi 15.5, ketika pengubah /std:c++17
diterapkan kode berikut sekarang menghasilkan peringatan C5041:
struct X {
static constexpr int size = 3;
};
const int X::size; // C5041: 'size': out-of-line definition for constexpr static data member is not needed and is deprecated in C++17
extern "C" __declspec(...)
peringatan C4768 sekarang aktif secara default
Peringatan ditambahkan di Visual Studio 2017 versi 15.3, tetapi tidak aktif secara default. Di Visual Studio 2017 versi 15.5, peringatan aktif secara default. Untuk informasi selengkapnya, lihat atribu Peringatan baru pada __declspec
.
Fungsi default dan __declspec(nothrow)
Pengompilasi sebelumnya mengizinkan fungsi default untuk dideklarasikan dengan __declspec(nothrow)
ketika fungsi dasar/anggota yang sesuai mengizinkan pengecualian. Perilaku ini bertentangan dengan standar C++ dan dapat menyebabkan perilaku yang tidak terdefinisi saat runtime. Standar mengharuskan fungsi tersebut didefinisikan sebagai dihapus jika ada ketidakcocokan spesifikasi pengecualian. Di bawah /std:c++17
, kode berikut meningkatkan C2280:
struct A {
A& operator=(const A& other) { // No exception specification; this function may throw.
...
}
};
struct B : public A {
__declspec(nothrow) B& operator=(const B& other) = default;
};
int main()
{
B b1, b2;
b2 = b1; // error C2280: attempting to reference a deleted function.
// Function was implicitly deleted because the explicit exception
// specification is incompatible with that of the implicit declaration.
}
Untuk memperbaiki kode ini, hapus __declspec(nothrow) dari fungsi default, atau hapus = default
dan berikan definisi untuk fungsi bersama dengan penanganan pengecualian yang diperlukan:
struct A {
A& operator=(const A& other) {
// ...
}
};
struct B : public A {
B& operator=(const B& other) = default;
};
int main()
{
B b1, b2;
b2 = b1;
}
noexcept
dan spesialisasi parsial
Dengan noexcept
dalam sistem jenis, spesialisasi parsial untuk mencocokkan jenis "dapat dipanggil" tertentu mungkin tidak dikompilasi, atau gagal memilih templat utama, karena spesialisasi parsial yang hilang untuk pointers-to-noexcept-functions.
Dalam kasus seperti itu, Anda mungkin perlu menambahkan lebih banyak spesialisasi parsial untuk menangani pointer fungsi noexcept
dan pointer noexcept
ke fungsi anggota. Kelebihan beban ini hanya legal dalam mode /std:c++17
atau yang lebih baru. Jika kompatibilitas mundur dengan C++14 harus dipertahankan, dan Anda menulis kode yang digunakan orang lain, Anda harus menjaga kelebihan beban baru ini di dalam arahan #ifdef
. Jika Anda bekerja dalam modul mandiri, dan bukan menggunakan pelindung #ifdef
, Anda dapat mengompilasinya dengan pengubah /Zc:noexceptTypes-
.
Kode berikut dikompilasi di bawah /std:c++14
tetapi gagal di bawah /std:c++17
dengan kesalahan C2027:
template <typename T> struct A;
template <>
struct A<void(*)()>
{
static const bool value = true;
};
template <typename T>
bool g(T t)
{
return A<T>::value;
}
void f() noexcept {}
int main()
{
return g(&f) ? 0 : 1; // C2027: use of undefined type 'A<T>'
}
Kode berikut berhasil di bawah /std:c++17
karena pengompilasi memilih spesialisasi parsial baru A<void (*)() noexcept>
:
template <typename T> struct A;
template <>
struct A<void(*)()>
{
static const bool value = true;
};
template <>
struct A<void(*)() noexcept>
{
static const bool value = true;
};
template <typename T>
bool g(T t)
{
return A<T>::value;
}
void f() noexcept {}
int main()
{
return g(&f) ? 0 : 1; // OK
}
Peningkatan kesesuaian di 15.6
Dasar-Dasar Pustaka C++17 V1
P0220R1 menggabungkan Spesifikasi Teknis Dasar-Dasar Pustaka untuk C++17 ke dalam standar. Mencakup pembaruan untuk <experimental/tuple>
, <experimental/optional>
, <experimental/functional>
, <experimental/any>
, <experimental/string_view>
, <experimental/memory>
, <experimental/memory_resource>
, dan <experimental/algorithm>
.
C++17: Meningkatkan pengurangan argumen templat kelas untuk pustaka standar
P0739R0 Memindahkan adopt_lock_t
ke depan daftar parameter untuk scoped_lock
mengaktifkan penggunaan yang konsisten dari scoped_lock
. Izinkan konstruktor std::variant
untuk berpartisipasi dalam resolusi kelebihan beban dalam lebih banyak kasus, untuk mengaktifkan penugasan penyalinan.
Penyempurnaan kesesuaian di 15.7
C++17: Kata sandi ulang yang mewarisi konstruktor
P0136R1 menentukan bahwa deklarasi using
yang menamai konstruktor sekarang membuat konstruktor kelas dasar yang sesuai terlihat oleh inisialisasi kelas turunan, daripada mendeklarasikan konstruktor kelas yang lebih turunan. Kata sandi ini adalah perubahan dari C++14. Dalam Visual Studio 2017 versi 15.7 dan yang lebih baru, dalam /std:c++17
mode dan yang lebih baru, kode yang valid di C++14 dan menggunakan konstruktor warisan mungkin tidak valid, atau mungkin memiliki semantik yang berbeda.
Contoh berikut menunjukkan perilaku C++14:
struct A {
template<typename T>
A(T, typename T::type = 0);
A(int);
};
struct B : A {
using A::A;
B(int n) = delete; // Error C2280
};
B b(42L); // Calls B<long>(long), which calls A(int)
// due to substitution failure in A<long>(long).
Contoh berikut menunjukkan perilaku /std:c++17
dalam Visual Studio 15.7:
struct A {
template<typename T>
A(T, typename T::type = 0);
A(int);
};
struct B : A {
using A::A;
B(int n)
{
//do something
}
};
B b(42L); // now calls B(int)
Untuk informasi selengkapnya, lihat Konstruktor.
C++17: Inisialisasi agregat yang diperluas
Jika konstruktor kelas dasar nonpublik, tetapi dapat diakses oleh kelas turunan, di bawah mode /std:c++17
dan yang lebih baru di Visual Studio 2017 versi 15.7, Anda tidak bisa lagi menggunakan kurung kurawal kosong untuk menginisialisasi objek dari tipe turunan.
Contoh berikut menunjukkan perilaku yang sesuai dengan C++14:
struct Derived;
struct Base {
friend struct Derived;
private:
Base() {}
};
struct Derived : Base {};
Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // OK in C++14: Calls Derived::Derived()
// which can call Base ctor.
Di C++17, Derived
sekarang dianggap sebagai jenis agregat. Ini berarti bahwa inisialisasi Base
melalui konstruktor default privat terjadi secara langsung, sebagai bagian dari aturan inisialisasi agregat yang diperluas. Sebelumnya, konstruktor privat Base
dipanggil melalui konstruktor Derived
, dan berhasil karena deklarasi friend.
Contoh berikut menunjukkan perilaku C++17 di Visual Studio versi 15.7 dalam mode /std:c++17
:
struct Derived;
struct Base {
friend struct Derived;
private:
Base() {}
};
struct Derived : Base {
Derived() {} // add user-defined constructor
// to call with {} initialization
};
Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // error C2248: 'Base::Base': cannot access
// private member declared in class 'Base'
C++17: Mendeklarasikan parameter template nonjenis dengan otomatis
Dalam mode /std:c++17
, pengompilasi sekarang dapat menyimpulkan jenis argumen template nonjenis yang dideklarasikan dengan auto
:
template <auto x> constexpr auto constant = x;
auto v1 = constant<5>; // v1 == 5, decltype(v1) is int
auto v2 = constant<true>; // v2 == true, decltype(v2) is bool
auto v3 = constant<'a'>; // v3 == 'a', decltype(v3) is char
Salah satu efek dari fitur baru ini adalah bahwa kode C++14 yang valid mungkin tidak valid atau mungkin memiliki semantik yang berbeda. Misalnya, beberapa kelebihan beban yang sebelumnya tidak valid sekarang valid. Contoh berikut menunjukkan kode C++14 yang dikompilasi karena panggilan ke example(p)
terikat ke example(void*);
. Dalam Visual Studio 2017 versi 15.7, dalam mode /std:c++17
, fungsi template example
adalah yang paling cocok.
template <int N> struct A;
template <typename T, T N> int example(A<N>*) = delete;
void example(void *);
void sample(A<0> *p)
{
example(p); // OK in C++14
}
Contoh berikut menunjukkan kode C++17 dalam Visual Studio 15.7 dalam mode /std:c++17
:
template <int N> struct A;
template <typename T, T N> int example(A<N>*);
void example(void *);
void sample(A<0> *p)
{
example(p); // C2280: 'int example<int,0>(A<0>*)': attempting to reference a deleted function
}
C++17: Konversi string dasar (parsial)
P0067R5 Tingkat rendah, fungsi lokal-independen untuk konversi antara bilangan bulat dan string dan antara angka floating-point dan string.
C++20: Menghindari peluruhan yang tidak perlu (parsial)
P0777R1 Menambahkan perbedaan antara konsep "peluruhan" dan hanya menghapus penentu const atau referensi. Sifat tipe baru remove_reference_t
menggantikan decay_t
dalam beberapa konteks. Dukungan untuk remove_cvref_t
diimplementasikan pada Visual Studio 2019.
C++17: Algoritma paralel
P0024R2 Paralelisme TS dimasukkan ke dalam standar, dengan sedikit modifikasi.
C++17: hypot(x, y, z)
P0030R1 Menambahkan tiga kelebihan baru ke std::hypot
, untuk jenis float
, double
, dan long double
, yang masing-masing memiliki tiga parameter masukan.
C++17: <filesystem>
P0218R1 Mengadopsi TS Sistem Berkas ke dalam standar dengan beberapa modifikasi kata.
C++17: Fungsi khusus matematika
P0226R1 Mengadopsi spesifikasi teknis sebelumnya untuk Fungsi Khusus Matematika ke dalam header <cmath>
standar.
C++17: Panduan pengurangan untuk pustaka standar
P0433R2 Pembaruan pada STL untuk memanfaatkan adopsi C++17 dari P0091R3, yang menambahkan dukungan untuk pengurangan argumen template kelas.
C++17: Memperbaiki konversi string dasar
P0682R1 Memindahkan fungsi konversi string dasar baru dari P0067R5 ke header baru <charconv>
dan membuat peningkatan lainnya, termasuk mengubah penanganan kesalahan untuk menggunakan std::errc
, bukan std::error_code
.
C++17: constexpr
untuk char_traits
(parsial)
P0426R1 Perubahan pada std::traits_type
fungsi anggota length
, compare
, dan find
untuk membuat std::string_view
dapat digunakan dalam ekspresi konstan. (Pada Visual Studio 2017 versi 15.6, hanya didukung untuk Clang/LLVM. Dalam versi 15.7, dukungan hampir selesai untuk ClXX juga.)
C++17: Argumen default dalam template kelas utama
Perubahan perilaku ini adalah prasyarat untuk P0091R3 - Template argument deduction for class templates
.
Sebelumnya, pengompilasi mengabaikan argumen default dalam template kelas utama:
template<typename T>
struct S {
void f(int = 0);
};
template<typename T>
void S<T>::f(int = 0) {} // Re-definition necessary
Dalam mode /std:c++17
dalam Visual Studio 2017 versi 15.7, argumen default tidak diabaikan:
template<typename T>
struct S {
void f(int = 0);
};
template<typename T>
void S<T>::f(int) {} // Default argument is used
Resolusi nama dependen
Perubahan perilaku ini adalah prasyarat untuk P0091R3 - Template argument deduction for class templates
.
Dalam contoh berikut, pengompilasi di Visual Studio 15.6 dan yang lebih lama menyelesaikan D::type
menjadi B<T>::type
di template kelas utama.
template<typename T>
struct B {
using type = T;
};
template<typename T>
struct D : B<T*> {
using type = B<T*>::type;
};
Visual Studio 2017 versi 15.7, dalam mode /std:c++17
, memerlukan kata kunci typename
dalam pernyataan using
di D. Tanpa typename
, pengompilasi memunculkan peringatan C4346: 'B<T*>::type': dependent name is not a type
dan kesalahan C2061: syntax error: identifier 'type'
:
template<typename T>
struct B {
using type = T;
};
template<typename T>
struct D : B<T*> {
using type = typename B<T*>::type;
};
C++17: [[nodiscard]]
atribut - peningkatan tingkat peringatan
Di Visual Studio 2017 versi 15.7 dalam mode /std:c++17
, tingkat peringatan C4834 ditingkatkan dari W3 ke W1. Anda dapat menonaktifkan peringatan dengan transmisi ke void
, atau dengan meneruskan /wd:4834
ke pengompilasi.
[[nodiscard]] int f() { return 0; }
int main() {
f(); // warning C4834: discarding return value
// of function with 'nodiscard'
}
Daftar inisialisasi kelas dasar konstruktor template variadik
Dalam edisi Visual Studio sebelumnya, daftar inisialisasi kelas dasar konstruktor template variadik yang hilang argumen templat secara keliru diizinkan tanpa kesalahan. Dalam Visual Studio 2017 versi 15.7, kesalahan pengompilasi dimunculkan.
Contoh kode berikut dalam Visual Studio 2017 versi 15.7 menimbulkan kesalahan C2614:
template<typename T>
struct B {};
template<typename T>
struct D : B<T>
{
template<typename ...C>
D() : B() {} // C2614: D<int>: illegal member initialization: 'B' is not a base or member
};
D<int> d;
Untuk memperbaiki kesalahan, ubah ekspresi B()
ke B<T>()
.
constexpr
inisialisasi agregat
Versi sebelumnya dari kompiler C++ salah menangani constexpr
inisialisasi agregat. Pengompilasi menerima kode yang tidak valid di mana aggregate-init-list memiliki terlalu banyak elemen, dan menghasilkan kode objek yang buruk untuk itu. Kode berikut adalah contoh kode tersebut:
#include <array>
struct X {
unsigned short a;
unsigned char b;
};
int main() {
constexpr std::array<X, 2> xs = { // C2078: too many initializers
{ 1, 2 },
{ 3, 4 }
};
return 0;
}
Di Visual Studio 2017 versi 15.7 pembaruan 3 dan yang lebih baru, contoh sebelumnya sekarang meningkatkan C2078. Contoh berikut menunjukkan cara menggunakan : Saat menginisialisasi std::array
dengan daftar kurung kurawal bersarang, berikan larik dalam daftar penyangganya sendiri:
#include <array>
struct X {
unsigned short a;
unsigned char b;
};
int main() {
constexpr std::array<X, 2> xs = {{ // note double braces
{ 1, 2 },
{ 3, 4 }
}}; // note double braces
return 0;
}
Peningkatan kesesuaian di 15.8
typename
pada pengidentifikasi yang tidak memenuhi syarat
Dalam mode /permissive-
, kata kunci typename
palsu pada pengidentifikasi yang tidak memenuhi syarat dalam definisi kerangka alias tidak lagi diterima oleh kompilator. Kode berikut sekarang menghasilkan C7511:
template <typename T>
using X = typename T; // C7511: 'T': 'typename' keyword must be
// followed by a qualified name
Untuk memperbaiki kesalahan, ubah baris kedua menjadi using X = T;
.
__declspec()
di sisi kanan definisi template alias
__declspec
tidak lagi diizinkan di sisi kanan definisi template alias. Sebelumnya, pengompilasi menerima tetapi mengabaikan kode ini. Ini tidak akan pernah menghasilkan peringatan penghentian ketika alias digunakan.
Atribut standar C++ [[deprecated]]
dapat digunakan sebagai gantinya, dan dihormati di Visual Studio 2017 versi 15.6. Kode berikut sekarang menghasilkan C2760:
template <typename T>
using X = __declspec(deprecated("msg")) T; // C2760: syntax error:
// unexpected token '__declspec',
// expected 'type specifier'`
Untuk memperbaiki kesalahan, ubah ke kode ke berikut (dengan atribut yang ditempatkan sebelum '=' dari definisi alias):
template <typename T>
using X [[deprecated("msg")]] = T;
Diagnostik pencarian nama dua fase
Pencarian nama dua fase mengharuskan nama nondependen yang digunakan dalam isi template harus terlihat oleh template pada waktu definisi. Sebelumnya, pengompilasi Microsoft C++ akan meninggalkan nama yang tidak berdasar karena tidak mencari hingga waktu instansiasi. Sekarang, itu mengharuskan nama yang tidak bergantung terikat di badan template.
Salah satu cara ini dapat bermanifestasi adalah dengan pencarian ke kelas dasar dependen. Sebelumnya, pengompilasi mengizinkan penggunaan nama yang ditentukan dalam kelas dasar dependen. Itu karena mereka akan dicari selama waktu instansiasi ketika semua jenis diselesaikan. Sekarang kode tersebut diperlakukan sebagai kesalahan. Dalam kasus ini, Anda dapat memaksa variabel untuk dicari pada waktu instansiasi dengan memenuhi syarat dengan jenis kelas dasar atau membuatnya tergantung, misalnya, dengan menambahkan pointer this->
.
Dalam mode /permissive-
, kode berikut sekarang menaikkan C3861:
template <class T>
struct Base {
int base_value = 42;
};
template <class T>
struct S : Base<T> {
int f() {
return base_value; // C3861: 'base_value': identifier not found
}
};
Untuk memperbaiki kesalahan, ubah pernyataan return
menjadi return this->base_value;
.
Catatan
Di versi pustaka Boost.Python sebelum 1.70, ada solusi khusus MSVC untuk deklarasi penerusan template di unwind_type.hpp
. Di bawah mode /permissive-
mulai Visual Studio 2017 versi 15.8 (_MSC_VER==1915
), pengompilasi MSVC melakukan pencarian nama dependen argumen (ADL) dengan benar. Sekarang konsisten dengan pengompilasi lain, membuat solusi ini melindungi yang tidak perlu. Untuk menghindari kesalahan C3861: 'unwind_type': identifier not found
, perbarui pustaka Boost.Python Anda.
meneruskan deklarasi dan definisi di namespace std
Standar C++ tidak memungkinkan pengguna untuk menambahkan deklarasi atau definisi penerusan ke dalam namespace std
. Menambahkan deklarasi atau definisi ke namespace std
atau ke namespace dalam namespace std
sekarang menghasilkan perilaku yang tidak terdefinisi.
Pada beberapa waktu mendatang, Microsoft akan memindahkan lokasi tempat beberapa jenis pustaka standar ditentukan. Perubahan ini akan memutus kode yang ada yang menambahkan deklarasi penerusan ke namespace std
. Peringatan baru, C4643, membantu mengidentifikasi masalah sumber tersebut. Peringatan diaktifkan dalam mode /default
dan nonaktif secara default. Ini akan mempengaruhi program yang dikompilasi dengan /Wall
atau /WX
.
Kode berikut sekarang menaikkan C4643:
namespace std {
template<typename T> class vector; // C4643: Forward declaring 'vector'
// in namespace std is not permitted
// by the C++ Standard`
}
Untuk memperbaiki kesalahan, gunakan arahan #include
daripada deklarasi maju:
#include <vector>
Konstruktor yang mendelegasikan ke diri mereka sendiri
Standar C++ menunjukkan bahwa pengompilasi harus memancarkan diagnostik saat konstruktor yang mendelegasikan mendelegasikan ke dirinya sendiri. Pengompilasi Microsoft C++ dalam mode /std:c++17
dan /std:c++latest
sekarang memunculkan C7535.
Tanpa kesalahan ini, program berikut akan mengompilasi tetapi akan menghasilkan perulangan tak terbatas:
class X {
public:
X(int, int);
X(int v) : X(v){} // C7535: 'X::X': delegating constructor calls itself
};
Untuk menghindari perulangan tak terbatas, delegasikan ke konstruktor yang berbeda:
class X {
public:
X(int, int);
X(int v) : X(v, 0) {}
};
offsetof
dengan ekspresi konstan
offsetof
secara tradisional telah diimplementasikan menggunakan makro yang memerlukan reinterpret_cast
. Penggunaan ini ilegal dalam konteks yang memerlukan ekspresi konstan, tetapi kompiler Microsoft C++ secara tradisional mengizinkannya. Makro offsetof
yang dikirimkan sebagai bagian dari pustaka standar dengan benar menggunakan kompilator intrinsik (__builtin_offsetof
), tetapi banyak orang telah menggunakan trik makro untuk mendefinisikan offsetof
mereka sendiri.
Dalam Visual Studio 2017 versi 15.8, pengompilasi membatasi area yang dapat muncul operator ini reinterpret_cast
dalam mode default, untuk membantu kode sesuai dengan perilaku standar C++. Di bawah /permissive-
, batasannya bahkan lebih ketat. Menggunakan hasil offsetof
di tempat yang memerlukan ekspresi konstan dapat menghasilkan kode yang mengeluarkan peringatan C4644 atau C2975.
Kode berikut meningkatkan C4644 dalam mode default dan /std:c++17
, dan C2975 dalam mode /permissive-
:
struct Data {
int x;
};
// Common pattern of user-defined offsetof
#define MY_OFFSET(T, m) (unsigned long long)(&(((T*)nullptr)->m))
int main()
{
switch (0) {
case MY_OFFSET(Data, x): return 0; // C4644: usage of the
// macro-based offsetof pattern in constant expressions
// is non-standard; use offsetof defined in the C++
// standard library instead
// OR
// C2975: invalid template argument, expected
// compile-time constant expression
default: return 1;
}
}
Untuk memperbaiki kesalahan, gunakan offsetof
seperti yang didefinisikan melalui <cstddef>
:
#include <cstddef>
struct Data {
int x;
};
int main()
{
switch (0) {
case offsetof(Data, x): return 0;
default: return 1;
}
}
cv-qualifier pada kelas dasar tunduk pada ekspansi paket
Versi pengompilasi Microsoft C++ sebelumnya tidak mendeteksi bahwa kelas dasar memiliki kualifikasi cv jika juga tunduk pada ekspansi paket.
Di Visual Studio 2017 versi 15.8, dalam mode /permissive-
kode berikut memunculkan C3770:
template<typename... T>
class X : public T... { };
class S { };
int main()
{
X<const S> x; // C3770: 'const S': is not a valid base class
}
template
kata kunci dan penentu nama bersarang
Dalam mode /permissive-
, pengompilasi sekarang memerlukan kata kunci template
untuk mendahului nama-template ketika muncul setelah penentu nama bersarang dependen.
Kode berikut dalam mode /permissive-
sekarang memunculkan C7510:
template<typename T> struct Base
{
template<class U> void example() {}
};
template<typename T>
struct X : Base<T>
{
void example()
{
Base<T>::example<int>(); // C7510: 'example': use of dependent
// template name must be prefixed with 'template'
// note: see reference to class template instantiation
// 'X<T>' being compiled
}
};
Untuk memperbaiki kesalahan, tambahkan kata kunci template
ke pernyataan Base<T>::example<int>();
, seperti yang ditunjukkan pada contoh berikut:
template<typename T> struct Base
{
template<class U> void example() {}
};
template<typename T>
struct X : Base<T>
{
void example()
{
// Add template keyword here:
Base<T>::template example<int>();
}
};
Peningkatan kesesuaian di 15.9
Urutan evaluasi dari kiri ke kanan untuk operator ->*
, []
, >>
, dan <<
Mulai dari C++17, operan dari operator ->*
, []
, >>
, dan <<
harus dievaluasi dalam urutan kiri-ke-kanan. Ada dua kasus di mana pengompilasi tidak dapat menjamin urutan ini:
ketika salah satu ekspresi operand adalah objek yang diteruskan oleh nilai atau berisi objek yang diteruskan oleh nilai, atau
saat dikompilasi dengan menggunakan
/clr
, dan salah satu operand adalah bidang objek atau elemen array.
Pengompilasi memancarkan peringatan C4866 ketika tidak dapat menjamin evaluasi kiri-ke-kanan. Pengompilasi menghasilkan peringatan ini hanya jika /std:c++17
atau yang lebih baru ditentukan, karena persyaratan urutan kiri-ke-kanan dari operator ini diperkenalkan di C++17.
Untuk mengatasi peringatan ini, pertama-tama pertimbangkan apakah evaluasi kiri-ke-kanan dari operand diperlukan. Misalnya, mungkin diperlukan ketika evaluasi operand mungkin menghasilkan efek samping yang bergantung pada pesanan. Urutan pengoperasi dievaluasi tidak memiliki efek yang dapat diamati dalam banyak kasus. Jika urutan evaluasi harus kiri ke kanan, pertimbangkan apakah Anda dapat meneruskan operand dengan referensi const sebagai gantinya. Perubahan ini menghilangkan peringatan dalam sampel kode berikut:
// C4866.cpp
// compile with: /w14866 /std:c++17
class HasCopyConstructor
{
public:
int x;
HasCopyConstructor(int x) : x(x) {}
HasCopyConstructor(const HasCopyConstructor& h) : x(h.x) { }
};
int operator>>(HasCopyConstructor a, HasCopyConstructor b) { return a.x >> b.x; }
// This version of operator>> does not trigger the warning:
// int operator>>(const HasCopyConstructor& a, const HasCopyConstructor& b) { return a.x >> b.x; }
int main()
{
HasCopyConstructor a{ 1 };
HasCopyConstructor b{ 2 };
a>>b; // C4866 for call to operator>>
};
Pengidentifikasi dalam template alias anggota
Pengidentifikasi yang digunakan dalam definisi templat alias anggota harus dideklarasikan sebelum digunakan.
Di versi pengompilasi sebelumnya, kode berikut diizinkan. Di Visual Studio 2017 versi 15.9, dalam mode /permissive-
, kompiler memunculkan C3861:
template <typename... Ts>
struct A
{
public:
template <typename U>
using from_template_t = decltype(from_template(A<U>{})); // C3861:
// 'from_template': identifier not found
private:
template <template <typename...> typename Type, typename... Args>
static constexpr A<Args...> from_template(A<Type<Args...>>);
};
A<>::from_template_t<A<int>> a;
Untuk memperbaiki kesalahan, nyatakan from_template
sebelum from_template_t
.
Perubahan modul
Pada Visual Studio 2017, versi 15.9, pengompilasi meningkatkan C5050 setiap kali opsi baris perintah untuk modul tidak konsisten antara sisi pembuatan modul dan konsumsi modul. Dalam contoh berikut, ada dua sertifikat.
Di sisi konsumsi (main.cpp), opsi
/EHsc
tidak ditentukan.Versi C++ adalah
/std:c++17
di sisi pembuatan, dan/std:c++14
di sisi konsumsi.
cl /EHsc /std:c++17 m.ixx /experimental:module
cl /experimental:module /module:reference m.ifc main.cpp /std:c++14
Pengompilasi meningkatkan C5050 untuk kedua kasus ini:
warning C5050: Possible incompatible environment while
importing module 'm': mismatched C++ versions.
Current "201402" module version "201703".
Kompilator juga memunculkan C7536 setiap kali file .ifc
telah diubah. Header antarmuka modul berisi hash SHA2 dari konten di bawahnya. Saat mengimpor, file .ifc
di-hash, lalu dicentang dengan hash yang disediakan di header. Jika ini tidak cocok, kesalahan C7536 muncul:
error C7536: ifc failed integrity checks.
Expected SHA2: '66d5c8154df0c71d4cab7665bab4a125c7ce5cb9a401a4d8b461b706ddd771c6'
Pemesanan parsial yang melibatkan alias dan konteks yang tidak disimpulkan
Implementasi menyimpang dalam aturan pemesanan parsial yang melibatkan alias dalam konteks yang tidak disimpulkan. Dalam contoh berikut, GCC dan pengompilasi Microsoft C++ (dalam mode /permissive-
) menimbulkan kesalahan, sementara Clang menerima kode.
#include <utility>
using size_t = std::size_t;
template <typename T>
struct A {};
template <size_t, size_t>
struct AlignedBuffer {};
template <size_t len>
using AlignedStorage = AlignedBuffer<len, 4>;
template <class T, class Alloc>
int f(Alloc &alloc, const AlignedStorage<T::size> &buffer)
{
return 1;
}
template <class T, class Alloc>
int f(A<Alloc> &alloc, const AlignedStorage<T::size> &buffer)
{
return 2;
}
struct Alloc
{
static constexpr size_t size = 10;
};
int main()
{
A<void> a;
AlignedStorage<Alloc::size> buf;
if (f<Alloc>(a, buf) != 2)
{
return 1;
}
return 0;
}
Contoh sebelumnya meningkatkan C2668:
partial_alias.cpp(32): error C2668: 'f': ambiguous call to overloaded function
partial_alias.cpp(18): note: could be 'int f<Alloc,void>(A<void> &,const AlignedBuffer<10,4> &)'
partial_alias.cpp(12): note: or 'int f<Alloc,A<void>>(Alloc &,const AlignedBuffer<10,4> &)'
with
[
Alloc=A<void>
]
partial_alias.cpp(32): note: while trying to match the argument list '(A<void>, AlignedBuffer<10,4>)'
Divergensi implementasi adalah karena regresi dalam kata-kata standar C++. Resolusi untuk masalah inti 2235 menghapus beberapa teks yang akan memungkinkan kelebihan beban ini diurutkan. Standar C++ saat ini tidak menyediakan mekanisme untuk memerintahkan sebagian fungsi ini, sehingga dianggap ambigu.
Sebagai solusinya, sebaiknya Anda tidak mengandalkan pemesanan parsial untuk menyelesaikan masalah ini. Sebagai gantinya, gunakan SFINAE untuk menghapus kelebihan beban tertentu. Dalam contoh berikut, kami menggunakan kelas pembantu IsA
untuk menghapus kelebihan pertama ketika Alloc
adalah spesialisasi dari A
:
#include <utility>
using size_t = std::size_t;
template <typename T>
struct A {};
template <size_t, size_t>
struct AlignedBuffer {};
template <size_t len>
using AlignedStorage = AlignedBuffer<len, 4>;
template <typename T> struct IsA : std::false_type {};
template <typename T> struct IsA<A<T>> : std::true_type {};
template <class T, class Alloc, typename = std::enable_if_t<!IsA<Alloc>::value>>
int f(Alloc &alloc, const AlignedStorage<T::size> &buffer)
{
return 1;
}
template <class T, class Alloc>
int f(A<Alloc> &alloc, const AlignedStorage<T::size> &buffer)
{
return 2;
}
struct Alloc
{
static constexpr size_t size = 10;
};
int main()
{
A<void> a;
AlignedStorage<Alloc::size> buf;
if (f<Alloc>(a, buf) != 2)
{
return 1;
}
return 0;
}
Ekspresi ilegal dan tipe nonliteral dalam definisi fungsi template
Ekspresi ilegal dan jenis nonliteral sekarang didiagnosis dengan benar dalam definisi fungsi templat yang secara eksplisit dikhususkan. Sebelumnya, kesalahan tersebut tidak dipancarkan untuk definisi fungsi. Namun, ekspresi ilegal atau jenis nonliteral masih akan didiagnosis jika dievaluasi sebagai bagian dari ekspresi konstanta.
Dalam versi Visual Studio sebelumnya, kode berikut mengompilasi tanpa peringatan:
void g();
template<typename T>
struct S
{
constexpr void f();
};
template<>
constexpr void S<int>::f()
{
g(); // C3615 in 15.9
}
Dalam Visual Studio 2017 versi 15.9, kode menimbulkan kesalahan C3615:
error C3615: constexpr function 'S<int>::f' cannot result in a constant expression.
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of 'g'.
Untuk menghindari kesalahan, hapus constexpr
kualifikasi dari instansiasi eksplisit fungsi f()
.
Lihat juga
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk