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.7p1
Zgodnie z wartością w pliku w copy-list-initialization
kompilatorze 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 CString
metody 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::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 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 BitBlt
metody , 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 /Wall
programem ) 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:externConstexpr
kompilatora 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_set
któ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::map
elementu , 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 */
__declspec
atrybuty 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:
Przejdź do najnowszego zestawu Windows SDK dostarczonego z programem Visual Studio 2017 w wersji 15.5.
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 constexpr
zmienną , 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 noexcept
elementu 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++17
z 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++17
parametru 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
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
W /std:c++17
trybie kompilator może teraz wyłudzać typ argumentu szablonu innego niż typ zadeklarowany za pomocą auto
polecenia :
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
, double
i 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 length
składowych , compare
i 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 typename
polecenia 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 offsetof
elementu .
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
/clr
i 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ą A
klasy :
#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()
.