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 zapoznać się ze zmianami w programie Visual Studio 2022, zobacz Ulepszenia zgodności języka C++ w programie Visual Studio 2022. Aby zapoznać się ze zmianami 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 Visual C++ What's New 2003–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 niestatycznej inicjalizacji składowych danych (NSDMI) dla agregacji kompilator MSVC w programie Visual Studio 2017 jest teraz kompletny w przypadku 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 sfINAE wyrażeń w większej językach

Kompilator nadal ulepsza obsługę wyrażenia SFINAE. Jest to wymagane w przypadku odliczeń 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 jest tablicą lub klasą, która ma: żaden konstruktor dostarczony przez użytkownika, nie statyczne składowe danych, które są prywatne lub chronione, bez klas bazowych i żadnych funkcji wirtualnych. Począwszy od języka C++14, agregacje mogą zawierać inicjatory składowych. Aby uzyskać więcej informacji, zobacz Inicjatory składowych i agregacje.

C++14: rozszerzone constexpr

Wyrażenia zadeklarowane jako constexpr mogą teraz zawierać pewne rodzaje deklaracji, instrukcje if i switch, instrukcje pętli i mutacje obiektów, których okres istnienia rozpoczął się w constexpr ocenie wyrażenia. Nie ma już wymagania, że constexpr funkcja niestatyczna elementu członkowskiego musi być niejawnie const. Aby uzyskać więcej informacji, zobacz Relaksujące ograniczenia constexpr dotyczące 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 nowszych [[fallthrough]] atrybut może być używany w kontekście instrukcji switch jako wskazówka dla kompilatora, że zachowanie fall-through jest zamierzone. Ten atrybut uniemożliwia kompilatorowi wystawianie 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 sentinel jako używanego przez zakresy w range-v3 i ukończone, ale nie do końca opublikowane zakresy specyfikacji technicznej. Aby uzyskać więcej informacji, zobacz P0184R0 - Generalizing the Range-Based for Loop.

Inicjowanie listy kopiowania

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. N4594 13.3.1.7p1Zgodnie z wartością w pliku w copy-list-initializationkompilatorze jest wymagany do rozważenia jawnego konstruktora do rozpoznawania przeciążenia. Jednak w przypadku wybrania tego konkretnego przeciążenia należy zgłosić błąd.

Poniższe dwa przykłady kompilowane 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 naprawić błąd, użyj bezpośredniej inicjalizacji:

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

W programie Visual Studio 2015 kompilator błędnie potraktował inicjowanie copy-list-list 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 wybierze MyInt(23)pozycję . 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. Powodzenie w programie Visual Studio 2015 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 wydaje teraz poprawne ostrzeżenie dotyczące 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 operand po lewej stronie 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 naprawić błąd, zadeklaruj array::size() funkcję jako constexpr lub usuń constexpr kwalifikator z f.

Typy klas przekazane do funkcji wariadycznych

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 kopię bitową 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ą, która zwraca 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 CStringmetody należy użyć podanego operator LPCTSTR() obiektu do rzutowania CString obiektu do wskaźnika języka C oczekiwanego przez ciąg formatu.

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

Kwalifikatory cv w konstrukcji klasy

W programie Visual Studio 2015 kompilator czasami niepoprawnie 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 poprawić błąd, zadeklaruj operator int() jako const.

Sprawdzanie dostępu do kwalifikowanych nazw 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 może zakończyć się niepowodzeniem z powodu niedostępności nazwy. Mogło to spowodować awarię lub nieoczekiwane zachowanie w czasie wykonywania, ponieważ kompilator niepoprawnie nazwał nieprawidłowe przeciążenie operatora. W programie Visual Studio 2017 jest zgłaszany 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 można zauważyć, że 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. Dlatego jeśli specjalizacja nie zależna zostanie znaleziona w argumencie decltype , nie zostanie odroczona do momentu utworzenia wystąpienia. Jest on przetwarzany natychmiast, a wszystkie wynikowe błędy są diagnozowane w tym czasie.

W poniższym przykładzie pokazano taki błąd kompilatora, który jest zgłaszany w momencie 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 starszych wersjach 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 klas 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 inicjalizacja klasy wartości zawsze inicjuje elementy członkowskie. Domyślny konstruktor nie jest dozwolony. W programie Visual Studio 2017 domyślne inicjatory składowych 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. Samo obejście stało się problematyczne po default wprowadzeniu go jako słowa kluczowego w języku C++11. W programie Visual Studio 2017 usunięto 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 dla 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 w jednej deklaracji można przechowywać wartość z poszczególnymi nazwami składników, gdy wartość jest tablicą, lub std::tuple std::pairlub 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 z bazowego typu wyliczenia o określonym zakresie na sam wyliczenie. Konwersja jest dostępna, gdy jego 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 Wyliczenia.

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 asynchronicznie, zwłaszcza 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++ program nie jest już obsługiwany w typach bool . Aby uzyskać więcej informacji, zobacz P0002R1 - Remove Deprecated operator++(bool).

Usuwanie przestarzałego register słowa kluczowego

Słowo register 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 elementów członkowskich

W poprzednich wersjach programu Visual Studio kompilator w niektórych przypadkach nie może wyemitować błędu dla nieprawidłowo sformułowanych wywołań do usuniętego szablonu elementu członkowskiego. Te wywołania mogą spowodować awarie w czasie wykonywania. Poniższy kod generuje teraz kod 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 warunków pod kątem cech typu

Program Visual Studio 2017 w wersji 15.3 usprawnia sprawdzanie warunków wstępnych pod kątem cech typu, aby ściślej przestrzegać standardu. Jednym z takich sprawdzania jest możliwość przypisania. Poniższy kod tworzy kod 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 marshalingu zarządzanego

Wywoływanie z funkcji zarządzanych do funkcji natywnych wymaga marshalingu. Środowisko CLR wykonuje marshaling, ale nie rozumie semantyki języka C++. W przypadku przekazania obiektu natywnego według wartości CLR wywołuje konstruktor kopiujący 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ętym ctorem kopii 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, tak aby program wywołuje std::terminate natychmiast po wystąpieniu nieprawidłowo 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 środowiska 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ń wysł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 wbudowanej definicji funkcji składowej szablonu, która nie została zadeklarowana w klasie. Poniższy 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 do X. Nie można pobrać adresu this lub 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 potencjalnie 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 nieprawidłowo 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.

Stosowanie z projektowaniem składowych złożonych offsetof

W programie Visual Studio 2017 w wersji 15.3 użycie funkcji offsetof(T, m) m jest "obiektem projektowania składowych złożonych" powoduje wyświetlenie ostrzeżenia podczas kompilowania z opcją /Wall . Poniższy kod jest źle sformułowany i może potencjalnie 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 tak, aby nie używał polecenia offsetof:

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

Używanie z offsetof funkcją składową lub składową danych statycznych

W programie Visual Studio 2017 w wersji 15.3 użycie elementu offsetof(T, m) m odwołuje 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 naprawić błąd, zmień kod, aby nie wywoływać już niezdefiniowanego zachowania. Jest to nieodnośny kod, który jest niedozwolony w standardzie 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(...) są stosowane przed extern "C" specyfikacją połączenia. Wcześniej kompilator zignorował atrybut, co mogłoby 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 naprawić ostrzeżenie, najpierw umieść następujące polecenie extern "C" :

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 i wywołuje usunięte destruktory

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 elementem decltype. 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 wyda 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 Value.

Puste deklaracje

Program Visual Studio 2017 w wersji 15.3 ostrzega teraz przed pustymi deklaracjami dla wszystkich typów, a nie tylko wbudowanych typów. 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, komentarz lub usuń puste deklaracje. W przypadkach, gdy obiekt nienazwany ma mieć efekt uboczny (taki jak RAII), powinien mieć nazwę.

Ostrzeżenie jest wykluczone 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. W przypadku używania cechy typu wymagane jest użycie specjalnych składników zapisywania bibliotek w kompilatorze std::is_convertible<...> języka Microsoft C++. W poniższym przykładzie statyczne aseracje 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 poprawnie sformułowana:

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

Destruktory prywatne i std::is_constructible

Poprzednie wersje kompilatora zignorowały, czy destruktor był prywatny podczas podejmowania decyzji o wyniku .std::is_constructible Teraz je uważa. W poniższym przykładzie statyczne aseracje 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 niekonstruwalny. 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 w przypadku znalezienia wielu kandydatów przy użyciu 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 zamierzasz wywołać metodę ::f().

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

Deklaracje funkcji lokalnych ukrywają deklarację funkcji w otaczającym zakresie i wyłączają wyszukiwanie zależne od argumentów. Poprzednie wersje kompilatora zawsze robiły wyszukiwanie zależne od argumentów w tym przypadku. Potencjalnie może to 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 wyświetlanej 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 elementu członkowskiego 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 /Wallprogramem ) 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, aby mieć 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 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 zachowywana jako alias dla programu noexcept(true). Aby uzyskać więcej informacji, zobacz Usuwanie specyfikacji wyjątków dynamicznych i noexcept.

not_fn()

not_fn P0005R4 zastępuje wartości not1 i not2.

Zmienianie kolejności enable_shared_from_this

enable_shared_from_this P0033R1 dodano w języku C++11. Standard 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 zmodyfikować i wstawić 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::mapelementu , zmianie klucza i ponownym utworzeniu).

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 były 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 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 typ zwracany jest teraz zdefiniowany jako specjalizacja typu sparametryzowanego dla iteratora i typu NodeType kontenera.

Zmienne wbudowane dla biblioteki standardowej

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

Funkcje załącznika D przestarzałe

Załącznik D standardu C++ zawiera wszystkie funkcje, które zostały przestarzałe, w tym shared_ptr::unique(), <codecvt>i namespace std::tr1. Po ustawieniu opcji kompilatora /std:c++17 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 domyślnie usuwane w obszarze /std:c++17 i później.

Ulepszona zgodność dzięki <iostream> 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 do noexcept systemu typów oraz usunięcie specyfikacji dynamicznych wyjątków.

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 uporzą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

Procedury obsługi odwołania do tablicy lub typu funkcji nigdy nie są zgodne dla żadnego obiektu wyjątku. Kompilator poprawnie honoruje tę regułę i zgłasza ostrzeżenie poziomu 4, C4843. Nie pasuje on również do procedury obsługi char* literału ciągu lub 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 unika błędu:

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

std::tr1 Przestrzeń nazw jest przestarzała

Przestrzeń nazw niestandardowa 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 rozwiązać ten problem, 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 bez wnioskowania

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, usuwając pomijanie 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 */

__declspecatrybuty z połączeniem extern "C"

We wcześniejszych wersjach programu Visual Studio kompilator zignorował __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 domyślnie zostało 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: wersja 10.0.15063.0, znana 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 tworzyły 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 dostarczonego z programem 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ł constexpr zmienne połączenie 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ę domyślne.

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

Jeśli plik nagłówka zawiera zadeklarowaną extern constexprzmienną , należy ją oznaczyć __declspec(selectany) 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 zwracanym. We wcześniejszych wersjach programu Visual Studio kompilator niepoprawnie zezwalał na typy abstrakcyjne, co może prowadzić do nieprawidłowego rozpoznawania przeciążenia 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 i usuwanie specyfikacji wyjątków dynamicznych noexcept

W języku C++17 throw() jest aliasem dla noexceptelementu throw(<type list>) i throw(...) są usuwane, 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 w ogóle. Umożliwia aktualizowanie kodu źródłowego w taki sposób, aby był zgodny z językiem C++17 bez konieczności ponownego zapisywania 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 korzystania /std:c++17z polecenia , 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 następujący kod generuje teraz 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 domyślnie zostało 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 bazowe/członkowskie 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++17parametru następujący kod zgłasza 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 być kompilowane lub nie można wybrać szablonu podstawowego, ponieważ brakuje częściowej specjalizacji wskaźników do funkcji noexcept-.

W takich przypadkach może być konieczne dodanie bardziej 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ą legalne tylko w /std:c++17 trybie lub nowszym. Jeśli zgodność wsteczna z językiem C++14 musi być zachowana i piszesz kod używany przez inne osoby, należy chronić te nowe przeciążenia wewnątrz #ifdef dyrektyw. Jeśli pracujesz w module samodzielnym, 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ą specjalizację A<void (*)() noexcept>częściową:

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 w wersji 1

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

C++17: Poprawa odliczenia 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 więcej przypadkach, aby włączyć przypisanie kopiowania.

Ulepszenia zgodności w wersji 15.7

C++17: Zmiana kolejności dziedziczych 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ą języka C++14. W programie Visual Studio 2017 w wersji 15.7 lub nowszej kod, który jest prawidłowy w /std:c++17 języku C++14 i używa konstruktorów dziedzicujących, może być nieprawidłowy lub może mieć różne semantyki.

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: Rozszerzone inicjowanie agregacji

P0017R1

Jeśli konstruktor klasy bazowej nie jest publiczny, ale dostępny dla klasy pochodnej, wówczas /std:c++17 w 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ę powodzeniem 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 z automatycznym użyciem

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óżne semantyki. 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 na potrzeby konwersji między liczbami całkowitymi i ciągami oraz między liczbami zmiennoprzecinkowymi i ciągami.

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

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

C++17: Algorytmy równoległe

P0024R2 Równoległość TS jest włączona do standardu, z drobnymi modyfikacjami.

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

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

C++17: <filesystem>

P0218R1 stosuje system plików TS do standardu z kilkoma modyfikacjami sformułowań.

C++17: Matematyczne funkcje specjalne

P0226R1 przyjmuje poprzednie specyfikacje techniczne funkcji specjalnych matematycznych do nagłówka standardowego<cmath>.

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

P0433R2 aktualizacje biblioteki STL, aby korzystać z wdrażania P0091R3 języka C++17, co dodaje obsługę odliczenia argumentu szablonu klasy.

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

P0682R1 Przenieś nowe podstawowe funkcje konwersji ciągów 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)

P0426R1 Zmiany std::traits_type w funkcjach lengthskładowych , comparei find w celu wykorzystania std::string_view ich w wyrażeniach stałych. (W programie Visual Studio 2017 w wersji 15.6 obsługiwana jest 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 elementu P0091R3 - Template argument deduction for class templates.

Wcześniej kompilator zignorował domyślny argument 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 elementu P0091R3 - Template argument deduction for class templates.

W poniższym przykładzie kompilator w programie Visual Studio 15.6 i starszych wersjach D::type B<T>::type jest rozpoznawany jako 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 typenamepolecenia kompilator 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 — wzrost poziomu ostrzeżenia

W programie Visual Studio 2017 w wersji 15.7 w /std:c++17 trybie poziom ostrzeżenia C4834 jest zwiększany z W3 do W1. Możesz wyłączyć ostrzeżenie z rzutowania na void, lub przekazując /wd:4834 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 wariadowego, która nie zawiera argumentów szablonu, była błędnie dozwolona bez błędu. W programie Visual Studio 2017 w wersji 15.7 jest zgłaszany 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++ nieprawidłowo obsługiwały inicjowanie constexpr agregacji. Kompilator zaakceptował nieprawidłowy kod, w którym lista agregacji 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 obiektu 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 aliasu nie są już akceptowane przez kompilator. Poniższy kod generuje teraz kod 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ł ten kod, ale zignorował ten kod. Nigdy nie spowoduje to wycofania ostrzeżenia, gdy alias został użyty.

Zamiast tego można użyć standardowego atrybutu [[deprecated]] C++ i jest przestrzegany w programie Visual Studio 2017 w wersji 15.6. Poniższy kod generuje teraz kod 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ści szablonu były widoczne dla szablonu w czasie definicji. Wcześniej kompilator języka Microsoft C++ pozostawiłby nie odnalezioną nazwę, ponieważ nie wyglądała aż do czasu utworzenia wystąpienia. Teraz wymaga, aby nazwy inne niż zależne zostały powiązane w 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 sprawdzane podczas tworzenia wystąpienia, gdy wszystkie typy są rozpoznawane. Teraz kod jest traktowany jako błąd. W takich przypadkach można wymusić wyszukanie zmiennej w czasie tworzenia wystąpienia, kwalifikując ją za pomocą typu klasy bazowej lub w inny sposób zależną, na przykład przez dodanie this-> wskaźnika.

W /permissive- trybie następujący kod zgłasza teraz kod 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ę na return return this->base_value;.

Uwaga

W wersjach biblioteki Boost.Python przed wersją 1.70 istnieje obejście specyficzne dla MSVC dla deklaracji przekazywania szablonu w pliku 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, co sprawia, że 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. Dodanie deklaracji lub definicji do przestrzeni std nazw lub przestrzeni nazw w przestrzeni nazw std powoduje teraz niezdefiniowane zachowanie.

W pewnym momencie 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 za pomocą /Wall polecenia lub /WX.

Poniższy kod zgłasza teraz kod C4643:

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

Aby naprawić błąd, należy użyć #include dyrektywy, a nie deklaracji forward:

#include <vector>

Konstruktory, które delegują się do siebie

Standard C++ sugeruje, że kompilator powinien emitować diagnostykę, gdy delegujący konstruktor deleguje się do siebie. Kompilator języka Microsoft C++ w systemie /std:c++17 i /std:c++latest tryby zgłasza teraz język C7535.

Bez tego błędu zostanie skompilowany następujący program, 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 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_cast. To użycie jest niedozwolone w kontekstach, które wymagają wyrażenia stałego, ale kompilator Microsoft C++ tradycyjnie go zezwolił. Makro offsetof dostarczane w ramach standardowej biblioteki 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 zgodne ze standardowym zachowaniem języka C++. W obszarze /permissive-ograniczenia są jeszcze bardziej rygorystyczne. Użycie wyniku w offsetof miejscach wymagających stałych wyrażeń może spowodować kod, który wyświetla ostrzeżenie C4644 lub C2975.

Poniższy kod wywołuje C4644 w trybie domyślnym i /std:c++17 oraz C2975 /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;
    }
}

Aby naprawić błąd, użyj polecenia offsetof zgodnie z definicją 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 pakietu

Poprzednie wersje kompilatora 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 kod 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 chodzi o zależny specyfikator zagnieżdżonej nazwy.

Poniższy kod w /permissive- trybie zgłasza teraz 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

  • podczas kompilowania przy użyciu metody /clri jednym 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 skutków ubocznych zależnych od kolejności. Kolejność oceniania operandów nie ma zauważalnego efektu w wielu przypadkach. Jeśli kolejność oceny musi być od lewej do prawej, rozważ, czy zamiast tego możesz przekazać operandy przez odwołanie 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 w /permissive- trybie kompilator zgłasza 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 zgłasza język C5050 za każdym razem, 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) nie określono opcji /EHsc .

  • 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 zgłasza 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 wartości 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 wywoływany 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 problemu podstawowego 2235 usunęło jakiś tekst, który umożliwiłby uporządkowanie tych przeciążeń. Bieżący standard języka C++ nie zapewnia mechanizmu częściowego porządkowowania tych funkcji, dlatego są uważane za niejednoznaczne.

Jako obejście tego problemu zalecamy, aby nie polegać na częściowym porządkoweniu w celu rozwiązania tego problemu. 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 szablonów

Niedozwolone wyrażenia i typy niesłowne są teraz prawidłowo diagnozowane w definicjach funkcji szablonów, które są jawnie wyspecjalizowane. Wcześniej takie błędy nie były emitowane dla definicji funkcji. Jednak niedozwolone wyrażenie lub typ inny niż literał 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++