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

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::tuplestd::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 constexprperintah.

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

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:

  1. Beralih ke SDK Windows terbaru yang disertakan dengan rilis Visual Studio 2017 versi 15.5.

  2. 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_convertibleTipe 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

P0017R1

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

P0127R2

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

Kesesuaian bahasa Microsoft C/C++