Skip to content

Commit 45db651

Browse files
authored
Merge pull request #2538 from mikeblome/mb-164
updates for 16.4
2 parents a68b810 + 41270aa commit 45db651

File tree

1 file changed

+241
-2
lines changed

1 file changed

+241
-2
lines changed

docs/overview/cpp-conformance-improvements.md

Lines changed: 241 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "C++ conformance improvements"
3-
ms.date: "10/04/2019"
3+
ms.date: "12/04/2019"
44
description: "Microsoft C++ in Visual Studio is progressing toward full conformance with the C++20 language standard."
55
ms.technology: "cpp-language"
66
author: "mikeblome"
@@ -455,6 +455,245 @@ To avoid the errors in the previous example, use **bool** instead of **BOOL** co
455455
456456
The non-standard headers \<stdexcpt.h> and \<typeinfo.h> have been removed. Code that includes them should instead include the standard headers \<exception> and \<typeinfo>, respectively.
457457
458+
## <a name="improvements_164"></a> Conformance improvements in Visual Studio 2019 version 16.4
459+
460+
### Better enforcement of two-phase name lookup for qualified-ids in /permissive-
461+
462+
Two-phase name lookup requires that non-dependent names used in template bodies must be visible to the template at definition time. Previously, such names may have been found when the template is instantiated. This change makes it easier to write portable, conformant code in MSVC under the [/permissive-](../build/reference/permissive-standards-conformance.md) flag.
463+
464+
In Visual Studio 2019 version 16.4, with the **/permissive-** flag set, the following example produces an error because `N::f` is not visible when the `f<T>` template is defined:
465+
466+
```cpp
467+
template <class T>
468+
int f() {
469+
return N::f() + T{}; // error C2039: 'f': is not a member of 'N'
470+
}
471+
472+
namespace N {
473+
int f() { return 42; }
474+
}
475+
```
476+
477+
Typically, this can be fixed by including missing headers or forward-declaring functions or variables, as shown in the following example:
478+
479+
```cpp
480+
namespace N {
481+
int f();
482+
}
483+
484+
template <class T>
485+
int f() {
486+
return N::f() + T{};
487+
}
488+
489+
namespace N {
490+
int f() { return 42; }
491+
}
492+
```
493+
494+
### Implicit conversion of integral constant expressions to null pointer
495+
496+
The MSVC compiler now implements [CWG Issue 903](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#903) in conformance mode (/permissive-). This rule disallows implicit conversion of integral constant expressions (except for the integer literal '0') to null pointer constants. The following example produces C2440 in conformance mode:
497+
498+
```cpp
499+
int* f(bool* p) {
500+
p = false; // error C2440: '=': cannot convert from 'bool' to 'bool *'
501+
p = 0; // OK
502+
return false; // error C2440: 'return': cannot convert from 'bool' to 'int *'
503+
}
504+
```
505+
506+
To fix the error, use **nullptr** instead of **false**. Note that literal 0 is still allowed:
507+
508+
```cpp
509+
int* f(bool* p) {
510+
p = nullptr; // OK
511+
p = 0; // OK
512+
return nullptr; // OK
513+
}
514+
```
515+
516+
### Standard rules for types of integer literals
517+
518+
In conformance mode (enabled by [/permissive-](../build/reference/permissive-standards-conformance.md)), MSVC uses the standard rules for types of integer literals. Previously, decimal literals too large to fit in a signed 'int' were given type 'unsigned int'. Now such literals are given the next largest signed integer type, 'long long'. Additionally, literals with the 'll' suffix which are too large to fit in a signed type are given type 'unsigned long long'.
519+
520+
This can lead to different warning diagnostics being generated, and behavior differences for arithmetic operations performed on literals.
521+
522+
The following example shows the new behavior in Visual Studio 2019, version 16.4. The `i` variable is of type **unsigned int** and therefore the warning is raised. The high-order bits of the variable `j` are set to 0.
523+
524+
```cpp
525+
void f(int r) {
526+
int i = 2964557531; // warning C4309: truncation of constant value
527+
long long j = 0x8000000000000000ll >> r; // literal is now unsigned, shift will fill high-order bits with 0
528+
}
529+
```
530+
531+
The following example demonstrates how to keep the old behavior and thus avoid the warnings and run-time behavior change:
532+
533+
```cpp
534+
void f(int r) {
535+
int i = 2964557531u; // OK
536+
long long j = (long long)0x8000000000000000ll >> r; // shift will keep high-order bits
537+
}
538+
```
539+
540+
### Function parameters that shadow template parameters
541+
542+
The MSVC compiler now raises an error when a function parameter shadows a template parameter:
543+
544+
```cpp
545+
template<typename T>
546+
void f(T* buffer, int size, int& size_read);
547+
548+
template<typename T, int Size>
549+
void f(T(&buffer)[Size], int& Size) // error C7576: declaration of 'Size' shadows a template parameter
550+
{
551+
return f(buffer, Size, Size);
552+
}
553+
```
554+
555+
To fix the error, change the name of one of the parameters:
556+
557+
```cpp
558+
template<typename T>
559+
void f(T* buffer, int size, int& size_read);
560+
561+
template<typename T, int Size>
562+
void f(T (&buffer)[Size], int& size_read)
563+
{
564+
return f(buffer, Size, size_read);
565+
}
566+
```
567+
568+
### User-provided specializations of type traits
569+
570+
In compliance with the *meta.rqmts* subclause of the Standard, the MSVC compiler now raises an error when it encounters a user-defined specialization of one of the specified type_traits templates in the `std` namespace. Unless otherwise specified, such specializations result in undefined behavior. The following example has undefined behavior because it violates the rule, and the `static_assert` fails with error **C2338**.
571+
572+
```cpp
573+
#include <type_traits>
574+
struct S;
575+
576+
template<>
577+
struct std::is_fundamental<S> : std::true_type {};
578+
579+
static_assert(std::is_fundamental<S>::value, "fail");
580+
```
581+
582+
To avoid the error, define a struct that inherits from the desired type_trait, and specialize that:
583+
584+
```cpp
585+
#include <type_traits>
586+
587+
struct S;
588+
589+
template<typename T>
590+
struct my_is_fundamental : std::is_fundamental<T> {};
591+
592+
template<>
593+
struct my_is_fundamental<S> : std::true_type { };
594+
595+
static_assert(my_is_fundamental<S>::value, "fail");
596+
```
597+
598+
### Changes to compiler-provided comparison operators
599+
600+
The MSVC compiler now implements the following changes to comparison operators per [P1630R1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1630r1.html) when the [/std:c++latest](../build/reference/std-specify-language-standard-version.md) option is enabled:
601+
602+
The compiler will no longer rewrite expressions with `operator==` if they involve a return type that is not a **bool**. The following code now produces *error C2088: '!=': illegal for struct*:
603+
604+
```cpp
605+
struct U {
606+
operator bool() const;
607+
};
608+
609+
struct S {
610+
U operator==(const S&) const;
611+
};
612+
613+
bool neq(const S& lhs, const S& rhs) {
614+
return lhs != rhs;
615+
}
616+
```
617+
618+
To avoid the error, you must explicitly define the needed operator:
619+
620+
```cpp
621+
struct U {
622+
operator bool() const;
623+
};
624+
625+
struct S {
626+
U operator==(const S&) const;
627+
U operator!=(const S&) const;
628+
};
629+
630+
bool neq(const S& lhs, const S& rhs) {
631+
return lhs != rhs;
632+
}
633+
```
634+
635+
The compiler will no longer define a defaulted comparison operator if it is a member of a union-like class. The following example now produces *C2120: 'void' illegal with all types*:
636+
637+
```cpp
638+
#include <compare>
639+
640+
union S {
641+
int a;
642+
char b;
643+
auto operator<=>(const S&) const = default;
644+
};
645+
646+
bool lt(const S& lhs, const S& rhs) {
647+
return lhs < rhs;
648+
}
649+
```
650+
651+
To avoid the error, define a body for the operator:
652+
653+
```cpp
654+
#include <compare>
655+
656+
union S {
657+
int a;
658+
char b;
659+
auto operator<=>(const S&) const { ... }
660+
}; 
661+
662+
bool lt(const S& lhs, const S& rhs) {
663+
return lhs < rhs;
664+
}
665+
```
666+
667+
The compiler will no longer define a defaulted comparison operator if the class contains a reference member. The following code now produces *error C2120: 'void' illegal with all types*:
668+
669+
```cpp
670+
#include <compare>
671+
672+
struct U {
673+
int& a;
674+
auto operator<=>(const U&) const = default;
675+
};
676+
677+
bool lt(const U& lhs, const U& rhs) {
678+
return lhs < rhs;
679+
}
680+
```
681+
682+
To avoid the error, define a body for the operator:
683+
684+
```cpp
685+
#include <compare>
686+
687+
struct U {
688+
int& a;
689+
auto operator<=>(const U&) const { ... };
690+
};
691+
692+
bool lt(const U& lhs, const U& rhs) {
693+
return lhs < rhs;
694+
}
695+
```
696+
458697
## <a name="update_160"></a> Bug fixes and behavior changes in Visual Studio 2019
459698
460699
### Reinterpret_cast in a constexpr function
@@ -2814,7 +3053,7 @@ struct S
28143053
{
28153054
constexpr void f();
28163055
};
2817-
3056+
28183057
template<>
28193058
constexpr void S<int>::f()
28203059
{

0 commit comments

Comments
 (0)