MSVC C++20 and the /std:c++20 Switch

Daniel Griffing

We are excited to announce that in Visual Studio 2019 version 16.11, we have added the /std:c++20 switch to the set of language mode switches available. The addition of this switch indicates that we’ve reached a point of sufficient stabilization of the MSVC C++20 feature set for it be used in production, with full support in VS servicing updates.

This blog post focuses on describing our level of C++20 feature support, compiler-supported extensions, and the remaining feature set differences between MSVC and the ISO C++ standard as of Visual Studio 2019 version 16.11 and Visual Studio 2022 version 17.0

C++ Language Modes and Compatibility Guarantees

First introduced in Visual Studio 2015, the MSVC compiler has included C++ language mode switches to indicate the targeted level of standard conformance and we now support three stable language modes: /std:c++14, /std:c++17, /std:c++20 (as of VS 2019 v16.11) and one preview mode (/std:c++latest).

The stable modes indicate that features under those modes are ready for production use and have ABI compatibility guarantees. The /std:c++latest mode contains ISO C++ features without strong guarantees for compatibility, allowing iteration based upon issues identified in testing, ISO C++ standard changes, and community feedback which may impact ABI stability of those features. Once stabilized, features under /std:c++latest will be moved under an applicable stable mode.

Language Mode Language Mode enables strict-conformance (/permissive-) ABI Stable Notes
/std:c++14 No Yes See **Note A
/std:c++17 No Yes See **Note A
/std:c++20 Yes Yes See **Note B
/std:c++latest Yes No See **Note B

**Note A: Strict conformance mode is opt-in via the /permissive- compiler switch

**Note B: Some functionality such as C++20 Modules require strict-conformance mode to be enabled due to strong dependency on ISO C++ semantic behaviors. Compatibility mode (/permissive) is supported as an opt-in switch  with some C++20 functionality disabled.

C++20 Features added in VS 2019 v16.9 and later

Below is a summary of language and library C++20 features implemented since the last feature update.

A more detailed changelog is available for the STL on its GitHub repo including information on the awesome community contributors who have provided feature implementations and bug fixes to the STL

VS 2019 v16.9

C++20 Language Features

  • Implemented P0634R3 Down with typename!
  • Implemented P0840R2 [[no_unique_address]] attribute (please see below for more details)
  • Implemented P1064R0 Allowing virtual function calls in constant expressions
  • Implemented P1141R2 Yet another approach for constrained declarations
  • Implemented P1327R1 Allowing dynamic_cast, polymorphic typeid in constant expressions
  • Implemented P1668R1 Permitting unevaluated inline assembly in constexpr functions
  • Implemented P0784R7 More constexpr containers

C++20 Library Features

  • Implemented P0339R6 polymorphic_allocator<>
  • Implemented P0660R10 <stop_token> And jthread
  • Implemented P0768R1 Library Support For The Spaceship Comparison Operator <=>
  • Implemented P1007R3 assume_aligned()
  • Implemented P1020R1 Smart Pointer Creation With Default Initialization
  • Implemented P1771R1 [[nodiscard]] For Constructors

VS 2019 v16.10 & v16.11

C++20 Language Features

  • Implemented P1073R3 Immediate functions
  • Implemented P1143R2 constinit
  • Implemented P1353R0 Missing feature-test macros

C++20 Library Features

  • Implemented P0053R7<syncstream>
  • Implemented P0355R7<chrono> Calendars And Time Zones
  • Implemented P0408R7 Efficient Access To basic_stringbuf’s Buffer
  • Implemented P0466R5 Layout-Compatibility And Pointer-Interconvertibility Traits
  • Implemented P0475R1 Guaranteed Copy Elision For Piecewise Construction
  • Implemented P0591R4 Utility Functions For Uses-Allocator Construction
  • Implemented P0608R3 Improving variant’s Converting Constructor/Assignment
  • Implemented P0645R10 <format> Text Formatting
  • Implemented P0784R7 Library Support For More constexpr Containers
  • Implemented P0896R4 Ranges
  • Implemented P0980R1 constexpr std::string
  • Implemented P1004R2 constexpr std::vector
  • Implemented P1208R6 <source_location>
  • Implemented P1502R1 Standard Library Header Units
  • Implemented P1614R2 Adding Spaceship <=> To The Library

VS 2022 17.0 (still in Preview)

C++20 Language Features

  • Completed implementation of P0734R0 Concepts

C++20 Library DRs

  • Implemented P2325R3 Views Should Not Be Required To Be Default Constructible

ISO C++20  Continuing Work, Defect Reports, and Clarifications

As part of implementing C++20, there were some late discoveries which required changes to the ISO C++20 standard via the standard committee’s Defect Report (DR) process. This included Existing implementations (pre-DR) for these features are available under the /std:c++latest switch.  We’re also tracking the DRs and are implementing those issue resolutions under /std:c++latest. Our plan is to make these capabilities available under the /std:c++20 switch after implementation of the full set of Standard Library DRs has completed. Progress on these features can be tracked on the MSVC STL GitHub site through its C++20 DRs project.

In the compiler, we are working with ISO C++ and other toolchain vendors to clarify expectations around allowing virtual functions to be constexpr (P1064R0). There are a couple of possibilities for implementation, which have significant ABI implications as to whether this is implemented via vtable entry. In the interim, we have implemented two modes, under /experimental:constevalVfuncVtable and /experimental:constevalVfuncNoVtable, which implement the most likely resolutions to this ambiguity. Once a decision is made on how to proceed, we’ll bring that capability under /std:c++20 and /std:c++latest.

Additionally, there were some feature areas that were unintentionally partially implemented. We are working to get those areas filled. For VS 2022 v17.0, we have implemented the requires-expression portion of the Concepts feature (P0734R0), which is scheduled to ship in VS 2022 v17.0.  We’re also working to complete implementation of Core Issue 1581: “When are constexpr member functions defined?,” which is tracking towards inclusion in a VS 2022 update after v17.0.

MSVC Extensions and ABI

C++20 [[no_unique_address]]

Implementation of C++20 [[no_unique_address]] included a couple of additional challenges due to the ABI-breaking impact (changing object layout) of applying this optimization. This is problematic due to the MSVC compiler ignoring attributes that are not known, as allowed by the standard, resulting in scenarios where MSVC ABI compatibility guarantees would be broken for standard C++ code:

  • Compiling the same header/source under /std:c++17 and /std:c++20 would result in link-time incompatibilities due to object layout differences resulting in ODR violations.
  • Linking static libraries built with an older version of the MSVC compiler (VS 2015 through VS 2019 v16.8), within the v14x ABI-compatible family, would result in ODR violations and break our compatibility guarantees.

It was decided to hold-off on enabling the optimization for the [[no_unique_address]] attribute in the MSVC compiler until our next ABI breaking revision of the MSVC toolset, where it will be enabled across all language modes.

However, we do recognize that there are some customers who are in a position to take advantage of this optimization without worrying about linking binaries across versions of the MSVC toolset.  For this audience, we have made this optimization available in VS 2019 v16.9 and later through an extension attribute that affects optimization across all compiler language modes, [[msvc::no_unique_address]].

  • There are portability concerns for customers who require ABI-compatiblity between MSVC and Clang for the STL. Please see https://github.com/microsoft/STL/issues/1364 for more details.
  • This extension attribute enables this optimization under all C++ language modes (e. /std:c++14, /std:c++17, /std:c++20, /std:c++latest.
  • We strongly recommend that any usage of this attribute is guarded by an MSVC version-check as demonstrated in the below example:
#include <iostream>

#if _MSC_VER >= 1929 // VS2019 v16.10 and later (_MSC_FULL_VER >= 192829913 for VS 2019 v16.9)
// Works with /std:c++14 and /std:c++17, and performs optimization

#define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]

#else

// no-op in MSVC v14x ABI
#define NO_UNIQUE_ADDRESS /* [[no_unique_address]] */

#endif


struct Empty {};

struct A
{
    int a;
    NO_UNIQUE_ADDRESS Empty b;
};

int main()
{
    A inst{ 0 };

    // [[msvc::no_unique_address]] : sizeof(inst) == 4.
    // [[no_unique_address]] : sizeof(inst) == 8.
    std::cout << sizeof(inst) << "\n";
}

C++20 Coroutine support for C++14/C++17 (/await:strict)

The /await:strict option can be used in place of /await for C++20 compatible coroutine support in projects that build in C++14 or C++17 mode. In /await:strict mode library support is provided in <coroutine> and in the std namespace. For full clarity, this behavior is on-by-default under /std:c++20 without any /await* switch usage.

Strict mode disables language extensions present in /await that were not adopted into the C++20 standard. Use  of such features with /await:strict will result in a compiler error. Strict mode also implements coroutine behaviors such as promise parameter preview that are not available under /await due to binary compatibility issues with older releases.

Note: coroutine state objects obtained from coroutine_handle<T>::address() are not compatible between /await and /await:strict modes. Using coroutine_handle<T>::from_address() on an address obtained from a coroutine handle created in an incompatible mode will result in undefined behavior.

More Information

For Visual Studio changes (beyond the C++ toolset) and download links, see the VS 2019 Release Notes and  VS 2022 Preview Release Notes. You can report bugs through Developer Community, and you can also report STL bugs via microsoft/STL GitHub issues.

 

Posted in C++

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Andrew Shaitorov 0

    Hi! Something is still broken in the latest MSVC compiler. The code below prints 8 for me (instead of zero):

    #include 
    
    class ConstExprTest
    {
    	const char* _begin;
    	const char* _end;
    
    public:
    
    	constexpr ConstExprTest(const char* str) noexcept :
    		_begin(str),
    		_end(str)
    	{
    	}
    
    	size_t GetLength() const noexcept { return _end - _begin; }
    
    };
    
    ConstExprTest Test = "hello";
    
    int main()
    {
    	std::cout << Test.GetLength() << "\n";
    
    	return 0;
    }
    
    • Jennifer YaoMicrosoft employee 0

      Hi Andrew,

      This is a known bug. We did have an engineer working on a fix, but after a while we realized the problem was more complex than we’d previously thought. It’s currently still in our backlog, but we’ve prioritized other work since then.

      One way to work around this issue would be to compile with /GF (string pooling) enabled.

      I hope this helps.

      Jennifer Yao
      Visual C++ Team

Feedback usabilla icon