Constexpr in VS2015 Update 1

Cody Miller [MSFT]

Visual Studio 2015 RTM shipped with support for constant expressions as specified in the C++11 language standard. The release received lots of excellent feedback from our users and the C++ community. Using that feedback, we’ve been working on refining our implementation for VS 2015 Update 1. Our goal with VS 2015 Update 1 was to finish up the last significant feature work for C++11 constexpr and improve our implementation’s robustness. This blog post is going to provide some notes to explain where VS 2015 Update 1 puts us and where we’re going with constexpr support.

Static Initializers

VS 2015 shipped with a warning that indicates that the compiler can detect and use initializers for constexpr evaluation but will not statically emit these initializers. That is, although the compiler had enough information to emit fully instantiated types that could be loaded from the compiled executable, it didn’t emit the fully instantiated types. These types were instantiated and constructed at runtime, as most C++ objects traditionally have been.

The great news is that VS 2015 Update 1 now supports emitting static initializers! These types are fully instantiated when they’re loaded into memory, rather than running the code at runtime to initialize them. This was the last feature that we needed to implement for C++11 constexpr support and we’re excited to ship it with Update 1.

We should extend kudos to Tanveer Gani for the herculean work he’s done to make this feature ship with Update 1. Because of his work, Update 1 will be shipping with complete support for emitting static initializers for constexpr objects. It will also ship with partial support for constant initialization of objects of non-literal types that have constexpr constructors (as specified in section 3.6.2 of the C++ language standard). Specifically, types with virtual functions aren’t implemented yet.

Static initializers are an important part of implementing std::once_flag, which is used for std::call_once. Stephan calls this out in his blog post about improvements to the STL in VS 2015 RTM.

The reduction in code generated by VS 2015 Update 1 for runtime execution can be quite dramatic. I’d like to take some time to explore the behavior with some examples. The C++ source is shown first, followed by assembly code illustrating static initialization. The assembly for these code snippets was generated by invoking the C++ compiler with the /FAsc flag.

Example 1: Initialization of a Constexpr Object

We’ll start with a simple example – constructing a simple instance of a type with a constexpr constructor.

struct Point {
    constexpr Point(int x1, int y1)
    : x(x1), y(y1)

    int x;
    int y;
constexpr Point p1(10, 11);

First, the assembly generated by VS 2015 RTM for this snippet (for comparison):

; VS 2015 RTM asm
PUBLIC ??0Point@@QEAA@HH@Z            ; Point::Point
?p1@@3UPoint@@B DQ 01H DUP (?)        ; p1
_BSS      ENDS
text$di  SEGMENT
??__Ep1@@YAXXZ PROC                   ; `dynamic initializer for ‘p1”, COMDAT

; 8    : constexpr Point p1(10, 11);

        sub       rsp, 40             ; 00000028H
        mov       r8d, 11
        mov       edx, 10
        lea       rcx, OFFSET FLAT:?p1@@3UPoint@@B
        call      ??0Point@@QEAA@HH@Z ; Point::Point
        add       rsp, 40             ; 00000028H
        ret       0
??__Ep1@@YAXXZ ENDP                   ; `dynamic initializer for ‘p1”
text$di  ENDS

And now the assembly generated by VS 2015 Update 1:

; VS 2015 Update 1 asm
        DD        0aH                 ; p1
        DD        0bH

Notice that there is no initialization code in the assembly generated by VS 2015 Update 1. Running the C++ code under the Visual Studio debugger in VS 2015 Update 1 will expectedly not execute the constructor for Point.

Example 2: Initialization of Array of Constexpr Objects

Continuing with the definition of Point above, we’ll create an array of Points:

constexpr Point arr[] = { Point(2, 3), Point(5, 7), Point(11, 13) };

The generated assembly from VS 2015 Update 1 is excellent:

; VS 2015 Update 1 asm
                DD          02H  ; arr
                DD          03H
                DD          05H
                DD          07H
                DD          0bH
                DD          0dH

 Example 3: Initializing pointer and reference members of a constexpr object

This code snippet initializes a constexpr object with pointers and references to a global constexpr variable.

constexpr int I = 42;
struct A {
    const int& ref;
    const char *ptr;
    const char *&ref2;
    constexpr A(const char *p, const int& r)
    : ref(r), ptr(p), ref2{ptr}
constexpr A a{ "qwerty", I };

This sample actually causes an ICE in VS 2015 RTM, but generates delightfully terse assembly code in VS 2015 Update 1.

; VS 2015 Update 1 asm
?I@@3HB         DD        02aH
                DD        FLAT:?I@@3HB    ; a
                DD        FLAT:$SG2668
                DD        FLAT:?a@@3UA@@B+4
                DB        ‘qwerty’, 00H

Example 4: Initializing constexpr classes with base constructors

Even classes with complicated (non-virtual) inheritance can be initialized statically. I’m not going to list the VS 2015 RTM as it’s prohibitively long, but you can view the COD file yourself by compiling the snippet below with the /FAsc flag.

struct Empty {};
struct A {
     short i;
     constexpr A(int ii)
     : i(ii)
struct B {
    double d;
    constexpr B(double di)
    : d(di)
struct C : Empty, A, B {
    double x;
    constexpr C()
    : x(1.0), A(42), B(-1.0)
constexpr C c;

And the assembly generated by VS 2015 Update 1:

; VS 2015 Update 1 asm
?c@@3UC@@B DW       02aH                          ; c
                ORG $+6
                DQ          0bff0000000000000r    ; -1
                DQ          03ff0000000000000r    ; 1

 Example 5: Initializing a non-literal type

As mentioned above, some non-literal types that are initialized with constants can be statically initialized. In the sample below, the initialized supplied to the constexpr constructor is a constant, so Update 1 can statically initialize it. Note that the type has a destructor, which makes the type a non-literal type.

extern "C" int puts(const char*);
struct NonLiteralType {
const char *p;
constexpr NonLiteralType(const char *pp)
: p(pp)
~NonLiteralType() {
NonLiteralType nlt("qwerty");
int main(){}

The assembly generated in Update 1 does not place the object in the CONST segment, because it was not declared constexpr:


; VS 2015 Update 1 asm
$SG2669               DB          ‘qwerty’, 00H

?nlt@@3UNonLiteralType@@A DD FLAT:$SG2669     ; nlt

Destruction of the non-literal type object is done with a registered “atexit” function:

; VS 2015 Update 1 asm
CRT$XCU             SEGMENT
?nlt$initializer$@@3P6AXXZA DD FLAT:??__Fnlt@@YAXXZ ; nlt$initializer$
CRT$XCU             ENDS

text$yd SEGMENT
        PROC ; `dynamic atexit destructor for ‘nlt”, COMDAT
       push      ebp
       mov       ebp, esp
       mov       ecx, OFFSET ?nlt@@3UNonLiteralType@@A      ; nlt
       call      ??1NonLiteralType@@QAE@XZ ; NonLiteralType::~NonLiteralType
       pop       ebp
       ret       0
??__Fnlt@@YAXXZ ENDP ; `dynamic atexit destructor for ‘nlt”
text$yd ENDS

Quality Improvements

Alongside the static initializer work, we’ve fixed ~45 bugs related to constexpr usage. The majority of these bugs were reported to us by customers. Because we tried to prioritize customer issues, you should see improvements across the board when writing constexpr code rather than in any particular areas. The table below shows the bugs that we fixed. Thanks to everybody who filed bugs!

TitleConnect CustomerConnectID
[constexpr] Using final on the member variable’s class breaks constexprAepaerae1135313
Error C2131 when creating constexpr std::arrayAndrey Ashikhmin1574634
constexpr void pointer variables not treated as constantsanthonyw11609590
constexpr failure with std::arrayBrandon Kentel1604956
Constexpr causes Internal Compiler Errorcamhusmj381573435
Constexpr causes Internal Compiler Errorcamhusmj381570534
Constexpr produces wrong results [compared to LLVM]camhusmj381300591
Erroneous error C2131: expression did not evaluate to a constantcamhusmj381596224
MSVC 2015 believes constexpr member pointer is not constantDavid Majnemer1327934
MSVC 2015 crashes on pointer arithmetic in constexpr contextDavid Majnemer1420558
MSVC 2015 crashes trying to evaluate constexpr constructor
which initializes a reference
David Majnemer1404631
MSVC 2015 crashes trying to evaluate constexpr containing pointer to member functionDavid Majnemer1327996
MSVC 2015 incorrectly rejects constexpr array of unions accessDavid Majnemer1323869
MSVC 2015 incorrectly rejects pointer equality in constexpr contextDavid Majnemer1404624
MSVC 2015 materializes one constant instead of two in constexpr contextDavid Majnemer1404688
MSVC 2015 rejects initializing constexpr reference to temporary objectDavid Majnemer1404715
MSVC 2015 rejects lvalue conditional operator of type const int in constexpr contextDavid Majnemer1404674
MSVC 2015 rejects member pointer comparison in constexpr contextDavid Majnemer1401241
MSVC2015 rejects valid and accepts invalid constexpr static_castDavid Majnemer1330530
MSVC 2015 will not evaluate function-local static constexpr reference variable to
David Majnemer1404755
Failure to compile with valid use of ‘constexpr’dn3571311469
Compiller Failiure in constexpr statement on std::make_array proposal implementationFelix Petriconi1494444
`std::integral_constant<>` implicitly-defined default constructor and/or `operator value_type` not
Bogus error regarding returning the address of or a reference to a temporary when attempting aggregate initialization inside of a constexpr functionildjarn1498733
C++ – constexpr does not work with aggregate initializationildjarn1572056
C++ – constexpr does not work with delegating constructorsildjarn1579279
C++ – constexpr static member functions must be fully qualified when called during type definitionildjarn1579334
C++ – Internal compiler error with constexpr constructorildjarn1571950
[constexpr] bug in deducing constexpr of function pointerkoosw1378031
Failed in constexpr lambda workaroundmzer01673865
VC++2015 RTM – constexpr constructor errors with union members with bitfieldsOrvid King1571281
constexpr and recurring template cause fatal error C1001Pendenaor1711144
class static constexpr value is 0pmingkr1384724
constexpr delegating constructor doesn’t compileQuixotic Labs1229998
constexpr bug related to “char const*const” parametersRui Figueira (Cloudgine)1272743
[constexpr][regression][boost] VC++ internal compiler error for a non-type template instantiationSasha Sitnikov1577162
delegating constructor in constexpr ctor won’t compilesubmitting_bug_reports_is_too_damn_hard1463556
[Feedback] ICE when compiling this C/C++ code  
Bogus error C2131 “expression did not evaluate to a constant” triggered by variadic-recursive constexpr  
constexpr delegating constructors  
constexpr template function causes compilation failure with erroneous message when called from within struct template  
constexpr 4607 ICE triggered by “ptr ? 3 : 4” in a constexpr function  

Looking Forward

Even with the improvements to C++11 constexpr that are shipping with update 1, we still have some refinement to do on our implementation. There are ~30 bugs remaining on our backlog in this area, many related to pointers-to-members in constant expressions. There’s some quality work to do around array and string aliasing, and although Tanveer’s done a solid job of readying static initializers, we’re planning for some amount of incoming bug reports related to the change.

Essentially, all of this means that we’ll still be working on C++11 constexpr for a while longer, but the outstanding work is manageable. Our goal is to wrap this work up in time for the next Visual Studio update. The plan after that is to immediately dive into C++14 constexpr support.


Cody Miller

Visual C++ Team