Ulepszenia zgodności języka C++, zmiany zachowania i poprawki błędów w programie Visual Studio 2017

Język Microsoft C/C++ w programie Visual Studio (MSVC) wprowadza ulepszenia zgodności i poprawki błędów w każdej wersji. W tym artykule wymieniono ulepszenia według wersji głównej, a następnie według wersji. Aby przejść bezpośrednio do zmian dla określonej wersji, użyj listy poniżej w tym artykule.

Ten dokument zawiera listę zmian w programie Visual Studio 2017. Aby uzyskać przewodnik po zmianach w programie Visual Studio 2022, zobacz Ulepszenia zgodności języka C++ w programie Visual Studio 2022. Aby uzyskać przewodnik po zmianach w programie Visual Studio 2019, zobacz Ulepszenia zgodności języka C++ w programie Visual Studio 2019. Aby uzyskać pełną listę poprzednich ulepszeń zgodności, zobacz Artykuł Visual C++ What's New 2003 to 2015 (Co nowego w programie Visual C++ do 2015).

Ulepszenia zgodności w programie Visual Studio 2017 RTW (wersja 15.0)

Dzięki obsłudze uogólnionej constexpr i niestatycznych elementów członkowskich inicjowania danych (NSDMI) dla agregacji kompilator MSVC w programie Visual Studio 2017 jest teraz kompletny dla funkcji dodanych w standardzie C++14. Jednak kompilator nadal nie ma kilku funkcji ze standardów C++11 i C++98. Aby uzyskać bieżący stan kompilatora, zobacz zgodność języka Microsoft C/C++ .

C++11: Obsługa wyrażenia SFINAE w kolejnych bibliotekach

Kompilator nadal ulepsza obsługę wyrażenia SFINAE. Jest to wymagane w przypadku wnioskowania argumentów szablonu i podstawiania, gdzie decltype i constexpr wyrażenia mogą być wyświetlane jako parametry szablonu. Aby uzyskać więcej informacji, zobacz Ulepszenia wyrażenia SFINAE w programie Visual Studio 2017 RC.

C++14: NSDMI dla agregacji

Agregacja to tablica lub klasa, która ma: żaden konstruktor dostarczany przez użytkownika, nie statyczne elementy członkowskie danych, które są prywatne lub chronione, bez klas bazowych i brak funkcji wirtualnych. Począwszy od języka C++14, agregacje mogą zawierać inicjatory składowe. Aby uzyskać więcej informacji, zobacz Inicjatory i agregacje składowych.

C++14: Rozszerzone constexpr

Wyrażenia zadeklarowane jako constexpr mogą teraz zawierać pewne rodzaje deklaracji, jeśli i switch, instrukcje pętli i mutacje obiektów, których okres istnienia rozpoczął się w constexpr ocenie wyrażenia. Nie ma już wymogu, że constexpr funkcja niestatyczna elementu członkowskiego musi być niejawnie const. Aby uzyskać więcej informacji, zobacz Złagodzenie ograniczeń dotyczących constexpr funkcji.

C++17: Terse static_assert

parametr komunikatu dla static_assert parametru jest opcjonalny. Aby uzyskać więcej informacji, zobacz N3928: Rozszerzanie static_assert, wersja 2.

C++17: [[fallthrough]] atrybut

W /std:c++17 trybie i nowszym [[fallthrough]] atrybut może być używany w kontekście instrukcji switch jako wskazówkę dla kompilatora, który ma na celu zachowanie rezerwowe. Ten atrybut uniemożliwia kompilatorowi wydawanie ostrzeżeń w takich przypadkach. Aby uzyskać więcej informacji, zobacz P0188R0 - Wording for [[fallthrough]] attribute.

Uogólnione pętle oparte na for zakresie

Pętle oparte na for zakresie nie wymagają już tego begin() i end() zwracają obiekty tego samego typu. Ta zmiana umożliwia end() zwrócenie sentinela używanego przez zakresy w range-v3 i ukończonych, ale nie dość opublikowanych specyfikacji technicznych zakresów. Aby uzyskać więcej informacji, zobacz P0184R0 - Generalizing the Range-Based for Loop.

Inicjowanie kopii listy

Program Visual Studio 2017 poprawnie zgłasza błędy kompilatora związane z tworzeniem obiektów przy użyciu list inicjatora. Te błędy nie zostały przechwycone w programie Visual Studio 2015 i mogą prowadzić do awarii lub niezdefiniowanego zachowania środowiska uruchomieniowego. Zgodnie z parametrem N4594 13.3.1.7p1w programie kompilator copy-list-initializationjest wymagany do rozważenia jawnego konstruktora do rozpoznawania przeciążenia. Jednak w przypadku wybrania tego konkretnego przeciążenia należy zgłosić błąd.

W poniższych dwóch przykładach skompiluj się w programie Visual Studio 2015, ale nie w programie 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 &'

}

Aby poprawić błąd, użyj bezpośredniej inicjalizacji:

A a1{ 1 };
const A& a2{ 1 };

W programie Visual Studio 2015 kompilator błędnie potraktował inicjowanie kopiowania listy w taki sam sposób jak zwykłe inicjowanie kopiowania: rozważał tylko konwertowanie konstruktorów na potrzeby rozpoznawania przeciążeń. W poniższym przykładzie program Visual Studio 2015 wybiera pozycję MyInt(23). Program Visual Studio 2017 poprawnie zgłasza błąd.

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

Ten przykład jest podobny do poprzedniego, ale zgłasza inny błąd. Kończy się to powodzeniem w programie Visual Studio 2015 i kończy się niepowodzeniem w programie Visual Studio 2017 z językiem 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
}

Przestarzałe definicje typów

Program Visual Studio 2017 teraz wyświetla prawidłowe ostrzeżenie dla przestarzałych definicji typów zadeklarowanych w klasie lub struktur. Poniższy przykład kompiluje się bez ostrzeżeń w programie Visual Studio 2015. Tworzy C4996 w programie 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

Program Visual Studio 2017 poprawnie zgłasza błąd, gdy lewy operand operacji oceny warunkowej nie jest prawidłowy w constexpr kontekście. Poniższy kod kompiluje się w programie Visual Studio 2015, ale nie w programie Visual Studio 2017, gdzie zgłasza język 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
}

Aby poprawić błąd, zadeklaruj array::size() funkcję jako constexpr lub usuń constexpr kwalifikator z f.

Typy klas przekazywane do funkcji variadycznych

W programie Visual Studio 2017 klasy lub struktury przekazywane do funkcji variadycznej, takie jak printf muszą być trywialnie kopiowalne. Po przekazaniu takich obiektów kompilator po prostu wykonuje bitową kopię i nie wywołuje konstruktora ani destruktora.

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

Aby poprawić błąd, można wywołać funkcję składową zwracającą trywialnie skopiowalny typ,

    std::atomic<int> i(0);
    printf("%i\n", i.load());

lub użyć rzutowania statycznego, aby przekonwertować obiekt przed przekazaniem go:

    struct S {/* as before */} s(0);
    printf("%i\n", static_cast<int>(s))

W przypadku ciągów skompilowanych i zarządzanych przy użyciu CStringparametru należy użyć podanego operator LPCTSTR() obiektu do rzutowania CString obiektu do wskaźnika C oczekiwanego przez ciąg formatu.

CString str1;
CString str2 = _T("hello!");
str1.Format(_T("%s"), static_cast<LPCTSTR>(str2));

Kwalifikatory cv w budowie klas

W programie Visual Studio 2015 kompilator czasami nieprawidłowo ignoruje kwalifikator cv podczas generowania obiektu klasy za pomocą wywołania konstruktora. Ten problem może potencjalnie spowodować awarię lub nieoczekiwane zachowanie środowiska uruchomieniowego. Poniższy przykład kompiluje się w programie Visual Studio 2015, ale zgłasza błąd kompilatora w programie Visual Studio 2017:

struct S
{
    S(int);
    operator int();
};

int i = (const S)0; // error C2440

Aby naprawić błąd, zadeklaruj operator int() jako const.

Sprawdzanie dostępu w kwalifikowanych nazwach w szablonach

Poprzednie wersje kompilatora nie sprawdzały dostępu do kwalifikowanych nazw w niektórych kontekstach szablonu. Ten problem może zakłócać oczekiwane zachowanie SFINAE, w którym podstawianie ma zakończyć się niepowodzeniem z powodu niedostępności nazwy. Może to spowodować awarię lub nieoczekiwane zachowanie w czasie wykonywania, ponieważ kompilator niepoprawnie nazwał nieprawidłowe przeciążenie operatora. W programie Visual Studio 2017 zostanie zgłoszony błąd kompilatora. Określony błąd może się różnić, ale typowy błąd to C2672 , "nie znaleziono pasującej przeciążonej funkcji". Poniższy kod kompiluje się w programie Visual Studio 2015, ale zgłasza błąd w programie 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.
}

Brakujące listy argumentów szablonu

W programie Visual Studio 2015 i starszych kompilator nie zdiagnozował wszystkich brakujących list argumentów szablonu. Nie należy pamiętać, gdy brakujący szablon pojawił się na liście parametrów szablonu: na przykład gdy brakuje części domyślnego argumentu szablonu lub parametru szablonu innego niż typ. Ten problem może spowodować nieprzewidywalne zachowanie, w tym awarie kompilatora lub nieoczekiwane zachowanie środowiska uruchomieniowego. Poniższy kod kompiluje się w programie Visual Studio 2015, ale generuje błąd w programie 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

Aby obsługiwać wyrażenie-SFINAE, kompilator analizuje decltype teraz argumenty, gdy szablony są deklarowane, a nie tworzone wystąpienia. Tak więc, jeśli specjalizacja nie zależna zostanie znaleziona w argumencie decltype , nie zostanie odroczona do momentu utworzenia wystąpienia. Jest ona przetwarzana natychmiast, a wszelkie błędy wynikowe są diagnozowane w tym czasie.

W poniższym przykładzie pokazano taki błąd kompilatora, który jest zgłaszany w punkcie deklaracji:

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

Klasy zadeklarowane w anonimowych przestrzeniach nazw

Zgodnie ze standardem C++ klasa zadeklarowana wewnątrz anonimowej przestrzeni nazw ma wewnętrzne powiązania i oznacza to, że nie można jej wyeksportować. W programie Visual Studio 2015 i wcześniejszym ta reguła nie została wymuszona. W programie Visual Studio 2017 reguła jest częściowo wymuszana. W programie Visual Studio 2017 poniższy przykład zgłasza błąd C2201:

struct __declspec(dllexport) S1 { virtual void f() {} };
  // C2201 const anonymous namespace::S1::vftable: must have external linkage
  // in order to be exported/imported.

Domyślne inicjatory dla składowych klasy wartości (C++/CLI)

W programie Visual Studio 2015 i starszych kompilator zezwolił (ale zignorował) domyślny inicjator składowy dla elementu członkowskiego klasy wartości. Domyślna inicjowanie klasy wartości zawsze inicjuje elementy członkowskie. Konstruktor domyślny nie jest dozwolony. W programie Visual Studio 2017 domyślne inicjatory składowe zgłaszają błąd kompilatora, jak pokazano w tym przykładzie:

value struct V
{
    int i = 0; // error C3446: 'V::i': a default member initializer
               // isn't allowed for a member of a value class
};

Domyślne indeksatory (C++/CLI)

W programie Visual Studio 2015 i starszych kompilator w niektórych przypadkach błędnie zidentyfikował właściwość domyślną jako domyślny indeksator. Możliwe było obejście problemu przy użyciu identyfikatora default w celu uzyskania dostępu do właściwości. Obejście stało się problematyczne po default wprowadzeniu słowa kluczowego w języku C++11. W programie Visual Studio 2017 naprawiono błędy, które wymagały obejścia. Kompilator zgłasza teraz błąd, gdy default jest używany do uzyskiwania dostępu do właściwości domyślnej klasy.

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

W programie Visual Studio 2017 można uzyskać dostęp do obu właściwości Value według ich nazwy:

#using "class1.dll"

void f(ClassLibrary1::Class1 ^r1, ClassLibrary1::Class2 ^r2)
{
       r1->Value;
       r2->Value;
}

Ulepszenia zgodności w wersji 15.3

constexpr Lambdas

Wyrażenia lambda mogą być teraz używane w wyrażeniach stałych. Aby uzyskać więcej informacji, zobacz constexpr wyrażenia lambda w języku C++.

if constexpr w szablonach funkcji

Szablon funkcji może zawierać if constexpr instrukcje umożliwiające rozgałęzianie w czasie kompilacji. Aby uzyskać więcej informacji, zobacz if constexpr instrukcje.

Instrukcje wyboru z inicjatorami

Instrukcja if może zawierać inicjator, który wprowadza zmienną w zakresie bloku w samej instrukcji. Aby uzyskać więcej informacji, zobacz if instrukcje z inicjatorem.

[[maybe_unused]] i [[nodiscard]] atrybuty

Nowy atrybut [[maybe_unused]] wycisza ostrzeżenia, gdy jednostka nie jest używana. Atrybut [[nodiscard]] tworzy ostrzeżenie, jeśli zwracana wartość wywołania funkcji zostanie odrzucona. Aby uzyskać więcej informacji, zobacz Atrybuty w języku C++.

Używanie przestrzeni nazw atrybutów bez powtórzeń

Nowa składnia umożliwiająca włączenie tylko jednego identyfikatora przestrzeni nazw na liście atrybutów. Aby uzyskać więcej informacji, zobacz Atrybuty w języku C++.

Powiązania strukturalne

Teraz istnieje możliwość w jednej deklaracji do przechowywania wartości z poszczególnymi nazwami jego składników, gdy wartość jest tablicą, a std::tuple lub std::pair, lub ma wszystkie publiczne niestatyczne elementy członkowskie danych. Aby uzyskać więcej informacji, zobacz P0144R0 - Structured Bindings i Zwracanie wielu wartości z funkcji.

Reguły budowy wartości enum class

Istnieje teraz niejawna konwersja dla wyliczeń o określonym zakresie, które nie są zawężające. Konwertuje się z typu bazowego wyliczenia o określonym zakresie na sam wyliczenie. Konwersja jest dostępna, gdy jej definicja nie wprowadza modułu wyliczającego, a źródło używa składni inicjowania listy. Aby uzyskać więcej informacji, zobacz P0138R2 - Construction Rules for enum class Values i Wyliczenie.

Przechwytywanie *this według wartości

*this Obiekt w wyrażeniu lambda może być teraz przechwytywany przez wartość. Ta zmiana umożliwia scenariusze, w których lambda jest wywoływana równolegle i operacje asynchroniczne, szczególnie w przypadku nowszych architektur maszyn. Aby uzyskać więcej informacji, zobacz P0018R3 - Lambda Capture of *this by Value as [=,*this].

Usuwanie operator++ dla bool

operator++ nie jest już obsługiwane w typach bool . Aby uzyskać więcej informacji, zobacz P0002R1 - Remove Deprecated operator++(bool).

Usuwanie przestarzałego słowa kluczowego register

register Słowo kluczowe, wcześniej przestarzałe (i ignorowane przez kompilator), jest teraz usuwane z języka. Aby uzyskać więcej informacji, zobacz P0001R1 - Remove Deprecated Use of the register Keyword.

Wywołania do usuniętych szablonów składowych

W poprzednich wersjach programu Visual Studio kompilator w niektórych przypadkach nie może emitować błędu dla nieprawidłowych wywołań do usuniętego szablonu elementu członkowskiego. Te wywołania mogłyby spowodować awarie w czasie wykonywania. Poniższy kod tworzy teraz 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
}

Aby naprawić błąd, zadeklaruj i jako int.

Wstępne sprawdzanie pod kątem cech typów

Program Visual Studio 2017 w wersji 15.3 ulepsza testy wstępne pod kątem cech typów, aby ściślej przestrzegać standardu. Jedną z takich kontroli jest możliwość przypisania. Poniższy kod tworzy C2139 w programie Visual Studio 2017 w wersji 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

Nowe ostrzeżenie kompilatora i testy środowiska uruchomieniowego natywne dla zarządzanego marshalingu

Wywoływanie z funkcji zarządzanych do funkcji natywnych wymaga marshalingu. ClR wykonuje marshaling, ale nie rozumie semantyki języka C++. W przypadku przekazania obiektu natywnego według wartości clR wywołuje konstruktor-kopiowania obiektu lub używa BitBltmetody , co może spowodować niezdefiniowane zachowanie w czasie wykonywania.

Teraz kompilator emituje ostrzeżenie, jeśli wykryje ten błąd w czasie kompilacji: obiekt macierzysty z usuniętą kopią ctor zostanie przekazany między granicą natywną i zarządzaną według wartości. W takich przypadkach, w których kompilator nie wie w czasie kompilacji, wprowadza kontrolę środowiska uruchomieniowego, aby program był wywoływany std::terminate natychmiast po wystąpieniu źle sformułowanego marshalingu. W programie Visual Studio 2017 w wersji 15.3 następujący kod generuje ostrzeżenie 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.`
}

Aby naprawić błąd, usuń dyrektywę #pragma managed , aby oznaczyć obiekt wywołujący jako natywny i uniknąć marshalingu.

Ostrzeżenie interfejsu API eksperymentalnego dla winRT

Interfejsy API WinRT, które są wydawane na potrzeby eksperymentowania i opinii, są ozdobione elementem Windows.Foundation.Metadata.ExperimentalAttribute. W programie Visual Studio 2017 w wersji 15.3 kompilator generuje ostrzeżenie C4698 dla tego atrybutu. Kilka interfejsów API w poprzednich wersjach zestawu Windows SDK zostało już ozdobionych atrybutem i wywołania tych interfejsów API wyzwalają teraz to ostrzeżenie kompilatora. Nowsze zestawy SDK systemu Windows mają atrybut usunięty ze wszystkich wysłanych typów. Jeśli używasz starszego zestawu SDK, musisz pominąć te ostrzeżenia dla wszystkich wywołań wysyłanych typów.

Poniższy kod generuje ostrzeżenie 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

Aby wyłączyć ostrzeżenie, dodaj #pragma:

#pragma warning(push)
#pragma warning(disable:4698)

Windows::Storage::IApplicationDataStatics2::GetForUserAsync();

#pragma warning(pop)

Definicja pozawierszowa funkcji składowej szablonu

Program Visual Studio 2017 w wersji 15.3 generuje błąd dla definicji pozawierszowej funkcji składowej szablonu, która nie została zadeklarowana w klasie. Następujący kod generuje teraz błąd C2039:

struct S {};

template <typename T>
void S::f(T t) {} // C2039: 'f': is not a member of 'S'

Aby naprawić błąd, dodaj deklarację do klasy:

struct S {
    template <typename T>
    void f(T t);
};
template <typename T>
void S::f(T t) {}

Próba podjęcia this adresu wskaźnika

W języku C++ this jest prvalue wskaźnika typu X. Nie można pobrać adresu this ani powiązać go z odwołaniem lvalue. W poprzednich wersjach programu Visual Studio kompilator umożliwia obejście tego ograniczenia przy użyciu rzutowania. W programie Visual Studio 2017 w wersji 15.3 kompilator generuje błąd C2664.

Konwersja na niedostępną klasę bazową

Program Visual Studio 2017 w wersji 15.3 generuje błąd podczas próby przekonwertowania typu na klasę bazową, która jest niedostępna. Poniższy kod jest źle sformułowany i może spowodować awarię w czasie wykonywania. Kompilator tworzy teraz kod C2243 , gdy widzi kod podobny do następującego:

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

Argumenty domyślne nie są dozwolone w definicjach poza wierszem funkcji składowych

Argumenty domyślne nie są dozwolone w definicjach poza wierszem funkcji składowych w klasach szablonów. Kompilator wyświetli ostrzeżenie w obszarze /permissive, a twardy błąd w obszarze /permissive-.

W poprzednich wersjach programu Visual Studio następujący źle sformułowany kod może potencjalnie spowodować awarię środowiska uruchomieniowego. Program Visual Studio 2017 w wersji 15.3 generuje ostrzeżenie 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
{
    // ...
}

Aby naprawić błąd, usuń = false argument domyślny.

Używanie z projektatorem składowych złożonych offsetof

W programie Visual Studio 2017 w wersji 15.3 użycie offsetof(T, m) funkcji m jest "kompilatorem składowych złożonych" powoduje ostrzeżenie podczas kompilowania z opcją /Wall . Poniższy kod jest źle sformułowany i może spowodować awarię w czasie wykonywania. Program Visual Studio 2017 w wersji 15.3 generuje ostrzeżenie 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]);

Aby naprawić kod, wyłącz ostrzeżenie z pragma lub zmień kod, aby nie używał polecenia offsetof:

#pragma warning(push)
#pragma warning(disable: 4841)
constexpr auto off = offsetof(A, arr[2]);
#pragma warning(pop)

Korzystanie offsetof z funkcji składowej lub składowej danych statycznych

W programie Visual Studio 2017 w wersji 15.3 użycie offsetof(T, m) elementu m odnosi się do elementu członkowskiego danych statycznych lub funkcji składowej powoduje wystąpienie błędu. Następujący kod generuje błąd 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'

Ten kod jest źle sformułowany i może potencjalnie spowodować awarię w czasie wykonywania. Aby rozwiązać ten błąd, zmień kod, aby nie wywoływać już niezdefiniowanego zachowania. Jest to nieodnośny kod, który jest niedozwolony przez standard C++.

Nowe ostrzeżenie dotyczące __declspec atrybutów

W programie Visual Studio 2017 w wersji 15.3 kompilator nie ignoruje już atrybutów, jeśli __declspec(...) jest stosowany przed extern "C" specyfikacją łączenia. Wcześniej kompilator zignorował atrybut, który może mieć wpływ na środowisko uruchomieniowe. Po ustawieniu /Wall opcji i /WX następujący kod generuje ostrzeżenie C4768:

__declspec(noinline) extern "C" HRESULT __stdcall // C4768: __declspec attributes before linkage specification are ignored

Aby rozwiązać problem z ostrzeżeniem, najpierw umieść extern "C" następujące polecenie:

extern "C" __declspec(noinline) HRESULT __stdcall

To ostrzeżenie jest domyślnie wyłączone w programie Visual Studio 2017 w wersji 15.3 i ma wpływ tylko na kod skompilowany za pomocą polecenia /Wall/WX. Począwszy od programu Visual Studio 2017 w wersji 15.5, jest ona domyślnie włączona jako ostrzeżenie poziomu 3.

decltype wywołania do usuniętych destruktorów

W poprzednich wersjach programu Visual Studio kompilator nie wykrył, kiedy wywołanie usuniętego destruktora wystąpiło w kontekście wyrażenia skojarzonego z decltypeprogramem . W programie Visual Studio 2017 w wersji 15.3 następujący kod generuje błąd 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
}

Niezainicjowane zmienne const

Wersja RTW programu Visual Studio 2017 miała regresję: kompilator języka C++ nie wystawi diagnostyki dla niezainicjowanej const zmiennej. Ta regresja została naprawiona w programie Visual Studio 2017 w wersji 15.3. Poniższy kod generuje teraz ostrzeżenie C4132:

const int Value; // C4132: 'Value': const object should be initialized

Aby naprawić błąd, przypisz wartość do Valueelementu .

Puste deklaracje

Program Visual Studio 2017 w wersji 15.3 ostrzega teraz przed pustymi deklaracjami dla wszystkich typów, a nie tylko typów wbudowanych. Poniższy kod generuje teraz ostrzeżenie poziomu 2 C4091 dla wszystkich czterech deklaracji:

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

Aby usunąć ostrzeżenia, oznacz jako komentarz lub usuń puste deklaracje. W przypadkach, gdy nienazwany obiekt ma mieć efekt uboczny (taki jak RAII), powinien mieć nazwę.

Ostrzeżenie jest wykluczane w obszarze /Wv:18 i jest domyślnie włączone na poziomie ostrzeżenia W2.

std::is_convertible dla typów tablic

Poprzednie wersje kompilatora dały niepoprawne wyniki dla std::is_convertible typów tablic. Wymaga to autorów bibliotek w celu specjalnego przypadku kompilatora Microsoft C++ w przypadku używania std::is_convertible<...> cech typu. W poniższym przykładzie statyczne potwierdzenia są przekazywane we wcześniejszych wersjach programu Visual Studio, ale kończą się niepowodzeniem w programie Visual Studio 2017 w wersji 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> funkcja jest obliczana przez sprawdzenie, czy definicja funkcji wyimaginowanej jest prawidłowo sformułowana:

   To test() { return std::declval<From>(); }

Destruktory prywatne i std::is_constructible

Poprzednie wersje kompilatora ignorowały, czy destruktor był prywatny podczas podejmowania decyzji o wyniku .std::is_constructible Teraz je uważa. W poniższym przykładzie statyczne potwierdzenia są przekazywane we wcześniejszych wersjach programu Visual Studio, ale kończą się niepowodzeniem w programie Visual Studio 2017 w wersji 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);

Destruktory prywatne powodują, że typ jest nieskonstruwalny. std::is_constructible<T, Args...> jest obliczany tak, jakby następująca deklaracja została zapisana:

   T obj(std::declval<Args>()...)

To wywołanie oznacza wywołanie destruktora.

C2668: Niejednoznaczna rozdzielczość przeciążenia

Poprzednie wersje kompilatora czasami nie wykryły niejednoznaczności, gdy znalazł wielu kandydatów za pośrednictwem deklaracji i wyszukiwania zależnego od argumentów. Ten błąd może prowadzić do wybrania nieprawidłowego przeciążenia i nieoczekiwanego zachowania środowiska uruchomieniowego. W poniższym przykładzie program Visual Studio 2017 w wersji 15.3 poprawnie zgłasza 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
}

Aby naprawić kod, usuń instrukcję using N::f , jeśli chcesz wywołać metodę ::f().

C2660: lokalne deklaracje funkcji i wyszukiwanie zależne od argumentów

Deklaracje funkcji lokalnych ukrywają deklarację funkcji w zakresie otaczającym i wyłączają wyszukiwanie zależne od argumentów. Poprzednie wersje kompilatora zawsze robiły wyszukiwanie zależne od argumentów w tym przypadku. Może to potencjalnie prowadzić do nieoczekiwanego zachowania środowiska uruchomieniowego, jeśli kompilator wybrał nieprawidłowe przeciążenie. Zazwyczaj błąd jest spowodowany nieprawidłowym podpisem lokalnej deklaracji funkcji. W poniższym przykładzie program Visual Studio 2017 w wersji 15.3 poprawnie zgłasza 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);
}

Aby rozwiązać ten problem, zmień f(S) podpis lub usuń go.

C5038: kolejność inicjowania na listach inicjatorów

Składowe klasy są inicjowane w kolejności deklarowanej, a nie kolejności, w której są wyświetlane na listach inicjatorów. Poprzednie wersje kompilatora nie ostrzegały, gdy kolejność listy inicjatorów różniła się od kolejności deklaracji. Ten problem może prowadzić do niezdefiniowanego zachowania środowiska uruchomieniowego, jeśli inicjowanie jednego członka zależy od innego elementu członkowskiego na liście, który już jest inicjowany. W poniższym przykładzie program Visual Studio 2017 w wersji 15.3 (z programem /Wall) zgłasza ostrzeżenie 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;
};

Aby rozwiązać ten problem, rozmieść listę inicjatorów tak, aby miała taką samą kolejność jak deklaracje. Podobne ostrzeżenie jest zgłaszane, gdy jeden lub oba inicjatory odwołują się do składowych klasy bazowej.

To ostrzeżenie jest domyślnie wyłączone i dotyczy tylko kodu skompilowanego za pomocą polecenia /Wall.

Ulepszenia zgodności w wersji 15.5

Funkcje oznaczone znakiem [14] są dostępne bezwarunkowo nawet w /std:c++14 trybie.

Nowy przełącznik kompilatora dla polecenia extern constexpr

We wcześniejszych wersjach programu Visual Studio kompilator zawsze dawał constexpr zmienne połączenie wewnętrzne, nawet jeśli zmienna została oznaczona jako extern. W programie Visual Studio 2017 w wersji 15.5 nowy przełącznik /Zc:externConstexprkompilatora umożliwia poprawne i zgodne ze standardami zachowanie. Aby uzyskać więcej informacji, zobacz extern constexpr łączenie.

Usuwanie specyfikacji wyjątków dynamicznych

P0003R5 Specyfikacje wyjątków dynamicznych zostały uznane za przestarzałe w języku C++11. Funkcja została usunięta z języka C++17, ale (nadal) przestarzała throw() specyfikacja jest ściśle przechowywana jako alias dla programu noexcept(true). Aby uzyskać więcej informacji, zobacz Usuwanie specyfikacji wyjątków dynamicznych i noexcept.

not_fn()

P0005R4not_fn to zamiana elementów not1 i not2.

Zmienianie kolejności enable_shared_from_this

P0033R1enable_shared_from_this dodano element w języku C++11. Standardowa wersja C++17 aktualizuje specyfikację, aby lepiej obsługiwać niektóre przypadki rogu. [14]

Łączenie map i zestawów

P0083R3 Ta funkcja umożliwia wyodrębnianie węzłów z kontenerów asocjacyjnych (czyli map, , set, unordered_map), unordered_setktóre następnie można modyfikować i wstawiać z powrotem do tego samego kontenera lub innego kontenera, który używa tego samego typu węzła. (Typowy przypadek użycia polega na wyodrębnieniu węzła z std::mapobiektu , zmianie klucza i ponownym zainicjowaniu operacji).

Przestarzałe części biblioteki

P0174R2 Kilka funkcji standardowej biblioteki języka C++ zostało zastąpionych przez nowsze funkcje na przestrzeni lat lub inne nie zostały uznane za przydatne lub problematyczne. Te funkcje są oficjalnie przestarzałe w języku C++17.

Usuwanie obsługi alokatora w programie std::function

P0302R1 Przed C++17 szablon std::function klasy miał kilka konstruktorów, które miały argument alokatora. Jednak użycie alokatorów w tym kontekście było problematyczne, a semantyka była niejasna. Kontruktory problemu zostały usunięte.

Poprawki dla not_fn()

P0358R1 Nowe sformułowanie dla std::not_fn elementu zapewnia obsługę propagacji kategorii wartości w przypadku użycia w wywołaniu otoki.

shared_ptr<T[]>, shared_ptr<T[N]>

P0414R2 Scalanie shared_ptr zmian z podstaw biblioteki do języka C++17. [14]

Naprawianie shared_ptr tablic

P0497R0 Poprawki obsługi shared_ptr dla tablic. [14]

Wyjaśnienie insert_return_type

P0508R0 Kontenery asocjacyjne z unikatowymi kluczami, a kontenery bez kolejności z unikatowymi kluczami mają funkcję insert składową zwracającą zagnieżdżony typ insert_return_type. Ten zwracany typ jest teraz definiowany jako specjalizacja typu sparametryzowanego dla iteratora i typu NodeType kontenera.

Zmienne wbudowane dla biblioteki standardowej

W przypadku biblioteki P0607R0 kilka typowych zmiennych zadeklarowanych w bibliotece standardowej jest teraz zadeklarowanych w tekście.

Przestarzałe funkcje załącznika D

Załącznik D standardu C++ zawiera wszystkie przestarzałe funkcje, w tym shared_ptr::unique(), <codecvt>i namespace std::tr1. Po ustawieniu /std:c++17 opcji kompilatora lub nowszej prawie wszystkie standardowe funkcje biblioteki w załączniku D są oznaczone jako przestarzałe. Aby uzyskać więcej informacji, zobacz Standardowe funkcje biblioteki w załączniku D są oznaczone jako przestarzałe.

std::tr2::sys Przestrzeń nazw w programie <experimental/filesystem> domyślnie emituje ostrzeżenie /std:c++14 o wycofaniu i jest teraz domyślnie usuwane w obszarze /std:c++17 i później.

Ulepszona zgodność w systemie <iostream> dzięki unikaniu niestandardowego rozszerzenia (specjalizacje jawne w klasie).

Biblioteka standardowa używa teraz szablonów zmiennych wewnętrznie.

Biblioteka standardowa została zaktualizowana w odpowiedzi na zmiany kompilatora języka C++17. Aktualizacje obejmują dodanie elementu noexcept w systemie typów oraz usunięcie specyfikacji wyjątków dynamicznych.

Zmiana kolejności częściowej

Kompilator poprawnie odrzuca następujący kod i wyświetla prawidłowy komunikat o błędzie:

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

Problem w powyższym przykładzie polega na tym, że istnieją dwie różnice w typach (const vs. non-const i pack vs. non-pack). Aby wyeliminować błąd kompilatora, usuń jedną z różnic. Następnie kompilator może jednoznacznie porządkować funkcje.

template<typename... T>
int f(T* ...)
{
    return 1;
}

template<typename T>
int f(T&)
{
    return 2;
}

int main()
{
    int i = 0;
    f(&i);
}

Programy obsługi wyjątków

Programy obsługi odwołania do tablicy lub typu funkcji nigdy nie są zgodne z żadnym obiektem wyjątku. Kompilator poprawnie honoruje tę regułę i podnosi ostrzeżenie poziomu 4, C4843. Nie pasuje on również do procedury obsługi char* ciągu ani wchar_t* do literału ciągu, gdy /Zc:strictStrings jest używany.

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

Poniższy kod pozwala uniknąć błędu:

catch (int (*)[1]) {}

std::tr1 Przestrzeń nazw jest przestarzała

Przestrzeń nazw standardowa std::tr1 jest teraz oznaczona jako przestarzała zarówno w trybach C++14, jak i C++17. W programie Visual Studio 2017 w wersji 15.5 następujący kod wywołuje kod 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.

Aby naprawić błąd, usuń odwołanie do tr1 przestrzeni nazw:

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

Standardowe funkcje biblioteki w załączniku D są oznaczone jako przestarzałe

Po ustawieniu trybu lub nowszego /std:c++17 przełącznika kompilatora prawie wszystkie standardowe funkcje biblioteki w załączniku D są oznaczone jako przestarzałe.

W programie Visual Studio 2017 w wersji 15.5 następujący kod wywołuje kod 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.

Aby naprawić błąd, postępuj zgodnie z instrukcjami w tekście ostrzeżenia, jak pokazano w poniższym kodzie:

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

Zmienne lokalne nieużywanych

W programie Visual Studio 15.5 ostrzeżenie C4189 jest emitowane w kolejnych przypadkach, jak pokazano w poniższym kodzie:

void f() {
    char s[2] = {0}; // C4189. Either use the variable or remove it.
}
warning C4189: 's': local variable is initialized but not referenced

Aby naprawić błąd, usuń nieużywaną zmienną.

Komentarze jednowierszowe

W programie Visual Studio 2017 w wersji 15.5 ostrzeżenia C4001 i C4179 nie są już emitowane przez kompilator języka C. Wcześniej były emitowane tylko w ramach przełącznika kompilatora /Za . Ostrzeżenia nie są już potrzebne, ponieważ komentarze jednowierszowe są częścią standardu C od 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'

Jeśli kod nie musi być zgodny z poprzednimi wersjami, należy unikać ostrzeżenia przez usunięcie pomijania C4001 i C4179. Jeśli kod musi być zgodny z poprzednimi wersjami, pomiń tylko C4619.

/* C only */

#pragma warning(disable:4619)
#pragma warning(disable:4001)
#pragma warning(disable:4179)

// single line comment
/* single line comment */

__declspec atrybuty z extern "C" połączeniem

We wcześniejszych wersjach programu Visual Studio kompilator ignorował __declspec(...) atrybuty, gdy __declspec(...) został zastosowany przed specyfikacją extern "C" połączenia. To zachowanie spowodowało wygenerowanie kodu, którego użytkownik nie zamierzał, z możliwymi konsekwencjami środowiska uruchomieniowego. Ostrzeżenie C4768 zostało dodane w programie Visual Studio w wersji 15.3, ale było domyślnie wyłączone. W programie Visual Studio 2017 w wersji 15.5 ostrzeżenie jest domyślnie włączone.

__declspec(noinline) extern "C" HRESULT __stdcall // C4768
warning C4768: __declspec attributes before linkage specification are ignored

Aby naprawić błąd, umieść specyfikację połączenia przed atrybutem __declspec:

extern "C" __declspec(noinline) HRESULT __stdcall

To nowe ostrzeżenie C4768 jest podane w niektórych nagłówkach zestawu Windows SDK, które zostały dostarczone z programem Visual Studio 2017 15.3 lub starszym (na przykład w wersji 10.0.15063.0, znanej również jako zestaw RS2 SDK). Jednak późniejsze wersje nagłówków zestawu Windows SDK (w szczególności ShlObj.h i ShlObj_core.h) zostały naprawione, aby nie generować ostrzeżenia. Po wyświetleniu tego ostrzeżenia pochodzącego z nagłówków zestawu Windows SDK możesz wykonać następujące akcje:

  1. Przejdź do najnowszego zestawu Windows SDK dołączonego do programu Visual Studio 2017 w wersji 15.5.

  2. Wyłącz ostrzeżenie dotyczące #include instrukcji nagłówka zestawu Windows SDK:

   #pragma warning (push)
   #pragma warning(disable:4768)
   #include <shlobj.h>
   #pragma warning (pop)

extern constexpr Powiązania

We wcześniejszych wersjach programu Visual Studio kompilator zawsze dawał zmienne połączenie constexpr wewnętrzne nawet wtedy, gdy zmienna została oznaczona jako extern. W programie Visual Studio 2017 w wersji 15.5 nowy przełącznik kompilatora (/Zc:externConstexpr) umożliwia prawidłowe zachowanie zgodne ze standardami. Ostatecznie to zachowanie stanie się wartością domyślną.

extern constexpr int x = 10;
error LNK2005: "int const x" already defined

Jeśli plik nagłówka zawiera zadeklarowaną extern constexprzmienną , należy oznaczyć __declspec(selectany) ją tak, aby jego zduplikowane deklaracje zostały poprawnie połączone:

extern constexpr __declspec(selectany) int x = 10;

typeid Nie można używać w niepełnym typie klasy

We wcześniejszych wersjach programu Visual Studio kompilator niepoprawnie zezwolił na następujący kod, co powoduje potencjalnie nieprawidłowe informacje o typie. W programie Visual Studio 2017 w wersji 15.5 kompilator poprawnie zgłasza błąd:

#include <typeinfo>

struct S;

void f() { typeid(S); } //C2027 in 15.5
error C2027: use of undefined type 'S'

std::is_convertible typ docelowy

std::is_convertible wymaga, aby typ docelowy był prawidłowym typem zwrotnym. We wcześniejszych wersjach programu Visual Studio kompilator niepoprawnie zezwalał na typy abstrakcyjne, co może prowadzić do nieprawidłowego rozpoznawania przeciążeń i niezamierzonego zachowania środowiska uruchomieniowego. Poniższy kod teraz poprawnie zgłasza kod C2338:

#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

Aby uniknąć błędu, podczas używania is_convertible należy porównać typy wskaźników, ponieważ porównanie typu innego niż wskaźnik może zakończyć się niepowodzeniem, jeśli jeden typ jest abstrakcyjny:

#include <type_traits>

struct B { virtual ~B() = 0; };
struct D : public B { virtual ~D(); };

static_assert(std::is_convertible<D *, B *>::value, "fail");

Usuwanie specyfikacji wyjątku dynamicznego i noexcept

W języku C++17 throw() element jest aliasem dla noexceptthrow(<type list>) elementów i throw(...) jest usuwany, a niektóre typy mogą zawierać wartość noexcept. Ta zmiana może spowodować problemy ze zgodnością źródła z kodem zgodnym z językiem C++14 lub starszym. Przełącznik /Zc:noexceptTypes- może służyć do przywracania do wersji noexcept C++14 podczas korzystania z trybu C++17 ogólnie. Umożliwia zaktualizowanie kodu źródłowego tak, aby był zgodny z językiem C++17 bez konieczności ponownego pisania całego throw() kodu w tym samym czasie.

Kompilator diagnozuje teraz również bardziej niezgodne specyfikacje wyjątków w deklaracjach w trybie C++17 lub z /permissive- nowym ostrzeżeniem C5043.

Poniższy kod generuje C5043 i C5040 w programie Visual Studio 2017 w wersji 15.5 po zastosowaniu przełącznika /std:c++17 :

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

Aby usunąć błędy podczas nadal używania polecenia /std:c++17, dodaj /Zc:noexceptTypes- przełącznik do wiersza polecenia lub zaktualizuj kod tak, aby używał polecenia noexcept, jak pokazano w poniższym przykładzie:

void f() noexcept;
void f() noexcept { }
void g() noexcept(false);

struct A {
    virtual void f() noexcept;
};

struct B : A {
    virtual void f() noexcept { }
};

Zmienne wbudowane

Statyczne constexpr składowe danych są teraz niejawnie inline, co oznacza, że ich deklaracja w klasie jest teraz ich definicją. Użycie definicji pozawierszowej static constexpr dla elementu członkowskiego danych jest nadmiarowe, a teraz przestarzałe. W programie Visual Studio 2017 w wersji 15.5 po zastosowaniu przełącznika /std:c++17 jest teraz wyświetlany ostrzeżenie 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(...) ostrzeżenie C4768 teraz domyślnie włączone

Ostrzeżenie zostało dodane w programie Visual Studio 2017 w wersji 15.3, ale było domyślnie wyłączone. W programie Visual Studio 2017 w wersji 15.5 ostrzeżenie jest domyślnie włączone. Aby uzyskać więcej informacji, zobacz Nowe ostrzeżenie dotyczące __declspec atrybutów.

Funkcje domyślne i __declspec(nothrow)

Kompilator zezwolił wcześniej na deklarowanie __declspec(nothrow) domyślnych funkcji, gdy odpowiednie funkcje podstawowe/składowe zezwalają na wyjątki. To zachowanie jest sprzeczne ze standardem języka C++ i może powodować niezdefiniowane zachowanie w czasie wykonywania. Standard wymaga, aby takie funkcje były definiowane jako usunięte, jeśli występuje niezgodność specyfikacji wyjątku. W obszarze /std:c++17poniższy kod wywołuje kod 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.
}

Aby poprawić ten kod, usuń __declspec (nothrow) z domyślnej funkcji lub usuń = default i podaj definicję funkcji wraz z wymaganą obsługą wyjątków:

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 i częściowe specjalizacje

W noexcept systemie typów częściowe specjalizacje pasujące do określonych typów "wywoływanych" mogą nie zostać skompilowane lub nie można wybrać szablonu podstawowego z powodu braku częściowej specjalizacji wskaźników do noexcept-functions.

W takich przypadkach może być konieczne dodanie większej liczby częściowych specjalizacji w celu obsługi noexcept wskaźników funkcji i noexcept wskaźników do funkcji składowych. Te przeciążenia są tylko legalne w /std:c++17 trybie lub nowszym. Jeśli należy zachować zgodność z poprzednimi wersjami języka C++14 i piszesz kod używany przez inne osoby, należy chronić te nowe przeciążenia wewnątrz #ifdef dyrektyw. Jeśli pracujesz w samodzielnym module, zamiast używać strażników, możesz po prostu skompilować go za pomocą #ifdef przełącznika /Zc:noexceptTypes- .

Poniższy kod kompiluje się w obszarze /std:c++14/std:c++17 , ale kończy się niepowodzeniem z powodu błędu 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>'
}

Poniższy kod kończy /std:c++17 się pomyślnie, ponieważ kompilator wybiera nową częściową specjalizację 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
}

Ulepszenia zgodności w wersji 15.6

Podstawy biblioteki języka C++17

P0220R1 zawiera specyfikację techniczną podstaw biblioteki dla języka C++17 w standardzie. Obejmuje aktualizacje <experimental/tuple>elementów , , <experimental/functional><experimental/string_view><experimental/any><experimental/memory><experimental/optional>, <experimental/memory_resource>, i .<experimental/algorithm>

C++17: Poprawa wnioskowania argumentów szablonu klasy dla standardowej biblioteki

P0739R0 Przejdź adopt_lock_t do przodu listy parametrów, scoped_lock aby umożliwić spójne użycie elementu scoped_lock. Zezwalaj std::variant konstruktorowi na uczestnictwo w rozwiązywaniu przeciążenia w kolejnych przypadkach w celu włączenia przypisania kopiowania.

Ulepszenia zgodności w wersji 15.7

C++17: Zmiana kolejności dziedziczyjących konstruktorów

P0136R1 określa, że deklaracja, która nazywa konstruktor, teraz sprawia, że using odpowiednie konstruktory klasy bazowej są widoczne dla inicjalizacji klasy pochodnej, zamiast deklarować więcej konstruktorów klasy pochodnej. Zmiana kolejności jest zmianą z języka C++14. W programie Visual Studio 2017 w wersji 15.7 lub nowszej kod /std:c++17 , który jest prawidłowy w języku C++14 i używa konstruktorów dziedziczyjących, może być nieprawidłowy lub może mieć inną semantyka.

W poniższym przykładzie pokazano zachowanie języka 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).

W poniższym przykładzie pokazano /std:c++17 zachowanie w programie 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)

Aby uzyskać więcej informacji, zobacz Konstruktory.

C++17: Inicjowanie rozszerzonej agregacji

P0017R1

Jeśli konstruktor klasy bazowej nie jest publiczny, ale dostępny dla klasy pochodnej, w /std:c++17 trybie i nowszym w programie Visual Studio 2017 w wersji 15.7 nie można już użyć pustych nawiasów klamrowych do zainicjowania obiektu typu pochodnego. W poniższym przykładzie pokazano zachowanie zgodne z językiem 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.

W języku C++17 Derived jest teraz uważany za typ agregacji. Oznacza to, że inicjowanie za pośrednictwem prywatnego konstruktora domyślnego Base odbywa się bezpośrednio w ramach rozszerzonej reguły inicjowania agregacji. Base Wcześniej konstruktor prywatny był wywoływany za pośrednictwem konstruktora Derived i zakończył się pomyślnie z powodu deklaracji przyjaciela. W poniższym przykładzie pokazano zachowanie języka C++17 w programie Visual Studio w wersji 15.7 w /std:c++17 trybie:

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: Deklarowanie parametrów szablonu bez typu za pomocą funkcji automatycznego

P0127R2

W /std:c++17 trybie kompilator może teraz wyłudzać typ argumentu szablonu innego niż typ zadeklarowany za pomocą autopolecenia :

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

Jednym z efektów tej nowej funkcji jest to, że prawidłowy kod C++14 może być nieprawidłowy lub może mieć różną semantyka. Na przykład niektóre przeciążenia, które wcześniej były nieprawidłowe, są teraz prawidłowe. W poniższym przykładzie pokazano kod C++14, który kompiluje się, ponieważ wywołanie metody example(p) jest powiązane z .example(void*); W programie Visual Studio 2017 w wersji 15.7 w /std:c++17 trybie example szablon funkcji jest najlepszym dopasowaniem.

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
}

W poniższym przykładzie pokazano kod C++17 w programie Visual Studio 15.7 w /std:c++17 trybie:

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: Konwersje ciągów podstawowych (częściowe)

P0067R5 Funkcje niezależne od ustawień regionalnych niskiego poziomu do konwersji między liczbami całkowitymi i ciągami oraz między liczbami zmiennoprzecinkami i ciągami.

C++20: Unikanie niepotrzebnego rozpadu (częściowe)

P0777R1 Dodaje różnice między koncepcją "rozpadu" a po prostu usunięciem kwalifikatorów const lub odwołań. Nowa cecha remove_reference_tdecay_t typu zastępuje się w niektórych kontekstach. remove_cvref_t Obsługa programu jest implementowana w programie Visual Studio 2019.

C++17: Algorytmy równoległe

P0024R2 Usługa TS równoległości jest włączona do standardu z drobnymi modyfikacjami.

C++17: hypot(x, y, z)

P0030R1 Dodaje trzy nowe przeciążenia do std::hypotelementu , dla typów float, doublei long double, z których każdy ma trzy parametry wejściowe.

C++17: <filesystem>

P0218R1 Przyjmuje system plików TS do standardu z kilkoma modyfikacjami wyrazów.

C++17: Matematyczne funkcje specjalne

P0226R1 Przyjmuje poprzednie specyfikacje techniczne dla funkcji matematycznych specjalnych w nagłówku standardowym <cmath> .

C++17: Przewodniki dotyczące potrąceń dla standardowej biblioteki

P0433R2 Aktualizacje do biblioteki STL, aby skorzystać z wdrożenia języka C++17 P0091R3, co dodaje obsługę odliczenia argumentu szablonu klasy.

C++17: Naprawianie konwersji ciągów podstawowych

P0682R1 Przenieś nowe funkcje konwersji ciągów podstawowych z P0067R5 do nowego nagłówka <charconv> i wprowadź inne ulepszenia, w tym zmianę obsługi błędów do użycia std::errc zamiast std::error_code.

C++17: constexpr for char_traits (częściowe)

P0426R1std::traits_type Zmiany w funkcjach lengthskładowych , comparei find , std::string_view aby można było ich używać w wyrażeniach stałych. (W programie Visual Studio 2017 w wersji 15.6 obsługiwane tylko dla maszyn wirtualnych Clang/LLVM. W wersji 15.7 obsługa jest prawie kompletna dla środowiska ClXX.

C++17: Argument domyślny w szablonie klasy podstawowej

Ta zmiana zachowania jest warunkiem wstępnym dla P0091R3 - Template argument deduction for class templatesprogramu .

Wcześniej kompilator zignorował argument domyślny w szablonie klasy podstawowej:

template<typename T>
struct S {
    void f(int = 0);
};

template<typename T>
void S<T>::f(int = 0) {} // Re-definition necessary

W /std:c++17 trybie w programie Visual Studio 2017 w wersji 15.7 argument domyślny nie jest ignorowany:

template<typename T>
struct S {
    void f(int = 0);
};

template<typename T>
void S<T>::f(int) {} // Default argument is used

Rozpoznawanie nazw zależnych

Ta zmiana zachowania jest warunkiem wstępnym dla P0091R3 - Template argument deduction for class templatesprogramu .

W poniższym przykładzie kompilator w programie Visual Studio 15.6 i starszych jest rozpoznawany D::typeB<T>::type w szablonie klasy podstawowej.

template<typename T>
struct B {
    using type = T;
};

template<typename T>
struct D : B<T*> {
    using type = B<T*>::type;
};

Program Visual Studio 2017 w wersji 15.7 w /std:c++17 trybie wymaga typename słowa kluczowego w instrukcji using D. Bez typenamekompilator zgłasza ostrzeżenie C4346: 'B<T*>::type': dependent name is not a type i błąd 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]] atrybut — zwiększenie poziomu ostrzeżenia

W programie Visual Studio 2017 w wersji 15.7 w /std:c++17 trybie poziom ostrzeżenia C4834 został zwiększony z W3 do W1. Ostrzeżenie można wyłączyć za pomocą rzutowania do voidelementu lub przekazując /wd:4834 go do kompilatora.

[[nodiscard]] int f() { return 0; }

int main() {
    f(); // warning C4834: discarding return value
         // of function with 'nodiscard'
}

Lista inicjowania klasy bazowej konstruktora szablonu Variadic

W poprzednich wersjach programu Visual Studio lista inicjowania klasy bazowej konstruktora szablonu variadic, która nie miała argumentów szablonu, została błędnie dozwolona bez błędu. W programie Visual Studio 2017 w wersji 15.7 zostanie zgłoszony błąd kompilatora.

Poniższy przykład kodu w programie Visual Studio 2017 w wersji 15.7 zgłasza błąd 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;

Aby naprawić błąd, zmień wyrażenie na B()B<T>().

constexpr inicjowanie agregacji

Poprzednie wersje kompilatora języka C++ niepoprawnie obsługiwały inicjowanie constexpr agregacji. Kompilator zaakceptował nieprawidłowy kod, w którym lista agregacja-init-list miała zbyt wiele elementów i wygenerowała dla niego nieprawidłowy kod obiektu. Poniższy kod jest przykładem takiego kodu:

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

W programie Visual Studio 2017 w wersji 15.7 update 3 lub nowszej poprzedni przykład zgłasza teraz język C2078. W poniższym przykładzie pokazano, jak naprawić kod. Podczas inicjowania elementu std::array z zagnieżdżonym nawiasem klamrowym-init-list nadaj tablicy wewnętrznej własną listę nawiasów klamrowych:

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

Ulepszenia zgodności w wersji 15.8

typename w przypadku niekwalifikowanych identyfikatorów

W /permissive- trybie fałszywe typename słowa kluczowe dotyczące niekwalifikowanych identyfikatorów w definicjach szablonów aliasów nie są już akceptowane przez kompilator. Poniższy kod tworzy teraz C7511:

template <typename T>
using  X = typename T; // C7511: 'T': 'typename' keyword must be 
                       // followed by a qualified name

Aby naprawić błąd, zmień drugi wiersz na using X = T;.

__declspec() po prawej stronie definicji szablonu aliasu

__declspec nie jest już dozwolone po prawej stronie definicji szablonu aliasu. Wcześniej kompilator zaakceptował, ale zignorował ten kod. Nigdy nie spowoduje to wycofania ostrzeżenia, gdy został użyty alias.

Zamiast tego można użyć standardowego atrybutu [[deprecated]] C++ i jest szanowany w programie Visual Studio 2017 w wersji 15.6. Poniższy kod tworzy teraz C2760:

template <typename T>
using X = __declspec(deprecated("msg")) T; // C2760: syntax error:
                                           // unexpected token '__declspec',
                                           // expected 'type specifier'`

Aby naprawić błąd, zmień kod na następujący (z atrybutem umieszczonym przed "=" definicji aliasu):

template <typename T>
using  X [[deprecated("msg")]] = T;

Diagnostyka wyszukiwania nazw dwufazowych

Wyszukiwanie nazw dwufazowych wymaga, aby nazwy inne niż zależne używane w treściach szablonu były widoczne dla szablonu w czasie definicji. Wcześniej kompilator języka Microsoft C++ pozostawiłby nienadzorowaną nazwę, ponieważ nie wyglądała aż do czasu utworzenia wystąpienia. Teraz wymagane jest, aby nazwy inne niż zależne zostały powiązane z treścią szablonu.

Jednym ze sposobów, w jaki może to być manifest, jest wyszukiwanie w zależnych klasach bazowych. Wcześniej kompilator zezwolił na używanie nazw zdefiniowanych w zależnych klasach bazowych. Jest to spowodowane tym, że będą one wyglądały podczas tworzenia wystąpienia, gdy wszystkie typy są rozpoznawane. Teraz kod jest traktowany jako błąd. W takich przypadkach można wymusić wyszukiwanie zmiennej w czasie wystąpienia, kwalifikując ją za pomocą typu klasy bazowej lub w inny sposób, aby była zależna, na przykład przez dodanie this-> wskaźnika.

W /permissive- trybie następujący kod zgłasza teraz 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
    }
};

Aby naprawić błąd, zmień instrukcję return na return this->base_value;.

Uwaga

W wersji biblioteki Boost.Python przed 1.70 istnieje obejście specyficzne dla języka MSVC dla deklaracji przekazywania szablonu w programie unwind_type.hpp. W /permissive- trybie rozpoczynającym się w programie Visual Studio 2017 w wersji 15.8 (_MSC_VER==1915) kompilator MSVC poprawnie wykonuje wyszukiwanie nazw zależnych od argumentów (ADL). Jest ona teraz spójna z innymi kompilatorami, dzięki czemu ta ochrona obejścia jest niepotrzebna. Aby uniknąć błędu C3861: 'unwind_type': identifier not found, zaktualizuj bibliotekę Boost.Python.

przekazywanie deklaracji i definicji w przestrzeni nazw std

Standard C++ nie zezwala użytkownikowi na dodawanie deklaracji lub definicji do przestrzeni nazw std. Dodawanie deklaracji lub definicji do przestrzeni nazw std lub do przestrzeni nazw w przestrzeni nazw std powoduje teraz niezdefiniowane zachowanie.

W przyszłości firma Microsoft przeniesie lokalizację, w której zdefiniowano niektóre standardowe typy bibliotek. Ta zmiana spowoduje przerwanie istniejącego kodu, który dodaje deklaracje do przestrzeni nazw std. Nowe ostrzeżenie C4643 pomaga zidentyfikować takie problemy źródłowe. Ostrzeżenie jest włączone w /default trybie i jest domyślnie wyłączone. Będzie to miało wpływ na programy skompilowane z /Wall programem lub /WX.

Poniższy kod zgłasza teraz C4643:

namespace std {
    template<typename T> class vector;  // C4643: Forward declaring 'vector'
                                        // in namespace std is not permitted
                                        // by the C++ Standard`
}

Aby rozwiązać ten błąd, użyj #include dyrektywy zamiast deklaracji do przodu:

#include <vector>

Konstruktory, które delegują się do siebie

Standard C++ sugeruje, że kompilator powinien emitować diagnostykę podczas delegowania konstruktora do siebie. Kompilator języka Microsoft C++ w trybach /std:c++17 i /std:c++latest teraz zgłasza C7535.

Bez tego błędu następujący program zostanie skompilowany, ale wygeneruje pętlę nieskończoną:

class X {
public:
    X(int, int);
    X(int v) : X(v){} // C7535: 'X::X': delegating constructor calls itself
};

Aby uniknąć nieskończonej pętli, deleguj go do innego konstruktora:

class X {
public:

    X(int, int);
    X(int v) : X(v, 0) {}
};

offsetof z wyrażeniami stałymi

offsetof tradycyjnie zaimplementowano przy użyciu makra, które wymaga reinterpret_castelementu . To użycie jest nielegalne w kontekstach, które wymagają wyrażenia stałej, ale kompilator Języka C++ tradycyjnie go zezwolił. offsetof Makro dostarczane w ramach biblioteki standardowej poprawnie używa wewnętrznego kompilatora (__builtin_offsetof), ale wiele osób użyło sztuczki makra do zdefiniowania własnego offsetofelementu .

W programie Visual Studio 2017 w wersji 15.8 kompilator ogranicza obszary, które te reinterpret_cast operatory mogą pojawiać się w trybie domyślnym, aby ułatwić zachowanie kodu zgodnego ze standardowym zachowaniem języka C++. W obszarze /permissive-ograniczenia są jeszcze bardziej rygorystyczne. Użycie wyniku w offsetof miejscach, które wymagają wyrażeń stałych, może spowodować kod, który wystawia ostrzeżenie C4644 lub C2975.

Poniższy kod zgłasza język C4644 w trybach domyślnych i /std:c++17 oraz C2975 w /permissive- trybie:

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

Aby rozwiązać ten błąd, użyj offsetof wartości zdefiniowanej za pomocą polecenia <cstddef>:

#include <cstddef>

struct Data {
    int x;
};

int main()
{
    switch (0) {
    case offsetof(Data, x): return 0;
    default: return 1;
    }
}

kwalifikatory cv w klasach bazowych podlegających rozszerzaniu pakietów

Poprzednie wersje kompilatora języka Microsoft C++ nie wykryły, że klasa bazowa miała kwalifikatory cv, jeśli również podlega rozszerzeniu pakietu.

W programie Visual Studio 2017 w wersji 15.8 w /permissive- trybie następujący kod zgłasza 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 specyfikatory słów kluczowych i zagnieżdżonych nazw

W /permissive- trybie kompilator wymaga template teraz, aby słowo kluczowe poprzedzało nazwę-szablonu, jeśli jest to zależne zagnieżdżone-nazwa-specyfikator.

Poniższy kod w /permissive- trybie wywołuje teraz kod 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
    }
};

Aby naprawić błąd, dodaj template słowo kluczowe do instrukcji Base<T>::example<int>(); , jak pokazano w poniższym przykładzie:

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

Ulepszenia zgodności w wersji 15.9

Kolejność oceny od lewej do prawej dla operatorów ->*, [], >>i <<

Począwszy od języka C++17, operandy operatorów ->*, [], >>i << muszą być oceniane w kolejności od lewej do prawej. Istnieją dwa przypadki, w których kompilator nie może zagwarantować tej kolejności:

  • gdy jedno z wyrażeń operandu jest obiektem przekazywanym przez wartość lub zawiera obiekt przekazywany przez wartość lub

  • gdy jest kompilowany przy użyciu metody /clr, a jeden z operandów jest polem obiektu lub elementu tablicy.

Kompilator emituje ostrzeżenie C4866 , gdy nie może zagwarantować oceny od lewej do prawej. Kompilator generuje to ostrzeżenie tylko wtedy, gdy /std:c++17 zostanie określone lub nowsze, ponieważ w języku C++17 wprowadzono wymaganie dotyczące kolejności od lewej do prawej.

Aby rozwiązać to ostrzeżenie, należy najpierw rozważyć, czy konieczna jest ocena operandów od lewej do prawej. Na przykład może być konieczne, gdy ocena operandów może spowodować powstanie efektów ubocznych zależnych od kolejności. Kolejność oceniania operandów nie ma zauważalnego wpływu w wielu przypadkach. Jeśli kolejność oceny musi być od lewej do prawej, rozważ, czy można przekazać operandy za pomocą odwołania const. Ta zmiana eliminuje ostrzeżenie w poniższym przykładzie kodu:

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

Identyfikatory w szablonach aliasów elementów członkowskich

Przed użyciem należy zadeklarować identyfikator używany w definicji szablonu aliasu elementu członkowskiego.

W poprzednich wersjach kompilatora dozwolony był następujący kod. W programie Visual Studio 2017 w wersji 15.9 /permissive- kompilator podnosi 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;

Aby naprawić błąd, zadeklaruj from_template wartość przed from_template_t.

Zmiany modułów

W programie Visual Studio 2017 w wersji 15.9 kompilator podnosi język C5050 , gdy opcje wiersza polecenia dla modułów nie są spójne między stronami tworzenia modułu i zużycia modułu. W poniższym przykładzie występują dwa problemy:

  • Po stronie zużycia (main.cpp) /EHsc opcja nie jest określona.

  • Wersja języka C++ znajduje /std:c++17 się po stronie tworzenia i /std:c++14 po stronie zużycia.

cl /EHsc /std:c++17 m.ixx /experimental:module
cl /experimental:module /module:reference m.ifc main.cpp /std:c++14

Kompilator zgłasza język C5050 dla obu tych przypadków:

warning C5050: Possible incompatible environment while
importing module 'm': mismatched C++ versions.
Current "201402" module version "201703".

Kompilator wywołuje również C7536 za każdym razem, gdy .ifc plik został naruszony. Nagłówek interfejsu modułu zawiera skrót SHA2 zawartości poniżej. Podczas importowania .ifc plik jest skrótem, a następnie sprawdzany względem skrótu podanego w nagłówku. Jeśli te elementy nie są zgodne, zostanie zgłoszony błąd C7536:

error C7536: ifc failed integrity checks.
Expected SHA2: '66d5c8154df0c71d4cab7665bab4a125c7ce5cb9a401a4d8b461b706ddd771c6'

Częściowe porządkowanie obejmujące aliasy i konteksty niezwiązane

Implementacje różnią się w częściowych regułach porządkowania obejmujących aliasy w kontekstach niezwiązanych z dedukowaniem. W poniższym przykładzie GCC i kompilator Microsoft C++ (w /permissive- trybie) zgłaszają błąd, a język Clang akceptuje kod.

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

W poprzednim przykładzie jest zgłaszany kod 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>)'

Rozbieżność implementacji wynika z regresji w standardowym sformułowaniu języka C++. Rozwiązanie podstawowego problemu 2235 usunęło jakiś tekst, który umożliwiłby porządkowanie tych przeciążeń. Obecny standard języka C++ nie zapewnia mechanizmu częściowego porządkowowania tych funkcji, dlatego są uważane za niejednoznaczne.

Aby rozwiązać ten problem, zalecamy, aby rozwiązać ten problem, zalecamy, aby nie polegać na częściowym porządkoweniu. Zamiast tego użyj sfINAE, aby usunąć określone przeciążenia. W poniższym przykładzie użyjemy klasy IsA pomocniczej, aby usunąć pierwsze przeciążenie, gdy Alloc jest specjalizacją Aklasy :

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

Niedozwolone wyrażenia i typy niesłowne w definicjach funkcji szablonowych

Niedozwolone wyrażenia i typy niesłowne są teraz prawidłowo diagnozowane w definicjach funkcji szablonowych, które są jawnie wyspecjalizowane. Wcześniej takie błędy nie były emitowane dla definicji funkcji. Jednak niedozwolone wyrażenie lub typ niesłowny nadal byłyby diagnozowane, jeśli zostanie obliczone jako część wyrażenia stałego.

W poprzednich wersjach programu Visual Studio następujący kod kompiluje się bez ostrzeżenia:

void g();

template<typename T>
struct S
{
    constexpr void f();
};

template<>
constexpr void S<int>::f()
{
    g(); // C3615 in 15.9
}

W programie Visual Studio 2017 w wersji 15.9 kod zgłasza błąd 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'.

Aby uniknąć błędu, usuń constexpr kwalifikator z jawnego wystąpienia funkcji f().

Zobacz też

Zgodność języka Microsoft C/C++