Compiler improvements in VS 2015 Update 2

Andrew Pardoe

The C++ compiler team is excited for you to try out the compiler in Visual Studio 2015 Update 2 CTP 1. Since Update 1 we’ve made progress on being Standards-conformant for lot of C++11 and C++14 features. One particularly big milestone is that our standard library in Update 2 supports every C++ Standard Library feature that’s been voted into C++11, C++14, and C++17-so-far. Parser improvements were required to make that happen, in addition to a lot of great work from our libraries team. We’ve also fixed about 300 compiler bugs since Update 1, many of which address bad code generation in our compiler. We understand that breaking changes can be very impactful for developers and so we strive to make sure any changes that can affect currently working source code are minor and rare and try our best to document any areas where bug fixes might affect you along with potential fixes. For example, we changed how we parse expressions inside of decltypes to better support expression SFINAE. And we have a new warning for what was previously ill-formed code that should better help you diagnose memory issues with mismatched new/delete. For the whole list of parser improvements that may require source code changes along with suggestions of how to move your code forward, please visit the dedicated MSDN page: Breaking Changes in Visual C++ 2015 Update 2. Let’s dive into some of the feature improvements we’ve made in the compiler for Update 2. Many of these changes are helping our compiler to conform to existing C++11 and C++14 Standards features. And some of our changes are forward-looking; that is, we implemented features expected to be in the C++17 Standard. The full list we’ll go through in this blog post are constexpr, variable templates, order of initializer list, modules, and working with UTF8 – let’s dive in!

constexpr

We’re continuing to improve our constexpr support. This is a feature that allows you to express computations that run at compile time instead of at runtime. If you’re not familiar with constexpr, you should check out Cody Miller’s blog post about constexpr here: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/constexpr-in-vs2015-update-1. We did a lot of work in Update 1 to enable constexpr support for features in the STL but we’re still making progress on full C++11 and C++14 constexpr support.

Variable templates

Variable templates are a C++14 feature that we implemented to help our standard libraries ship C++17 features. If you’ve ever used std::is_same<T, U>::value you’ll really appreciate std::is_same_v. It’s the variable template alias of the std::is_same type trait. Variable templates allow you to remove ::value from C++11 code using type traits, e.g., is_signed<T>::value. In advanced cases, you can remove a C++11 workaround for having to declare a static data member of a wrapper class template that only exists so you have something to parameterize. So what are variable templates and how might you go about using them in your code? Variable templates are a fix to the standard that allows the declaration of a variable in a template declaration. You can define a parameterized constant, for example: template<typename T> constexpr T pi = T(3.1415926535897932385); and use it in a function that computes the area of a circle: template<typename T> T area_of_circle_with_radius(T r) { return pi<T> * r * r; } Previously a template declaration was only allowed to be a function, class, or alias. Now, in the MSVC compiler it can be a variable as well. NB: We’re still doing the work to support variable templates in IntelliSense in the VS IDE. We made an exception and shipped the feature because we had other dependencies on variable templates (i.e., the STL work mentioned above.)

Order of initializer list

An initializer list allows you to initialize data simply in a braced comma-separated list. For example, it’s easier to write this std::complex<double> z{1,2} than to allocate the double and then initialize its members. The Standard specifies that initializer lists must be evaluated in the order that they appear. For example, in a{x(), y(), z()} x() must be called before y() which must be called before z(). One place where this is really critical is in iterating over the elements of a tuple. We’ve gotten feedback about this not working in MSVC before, for example, here: https://gist.github.com/DrPizza/bf8cbdf63e7a9702e90a. In Update 2 we’re now calling initializer lists in order, according to the Standard. In places where we fail to do so—usually because of nontrivial dependencies between classes in the initializer list—we’ll emit a warning.

C++ Modules

Our team has been working hard on C++ Modules, a new C++ feature that has been approved by the C++ Evolution Working Group for a Technical Specification, and currently under review by the C++ Core Working Group. Modules help you improve the componentization of your code, which in turn leads to build throughput improvements. Modules also isolate your code from preprocessor state which can be hard to debug. And they’ll make it easier to develop tools to work with your code as you don’t have to write a C++ preprocessor and parser to understand your interfaces. We wrote an in-depth blog post about C++ Modules here: https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-modules-in-vs-2015-update-1. If you tried out C++ Modules in Update 1, you might have noticed that a lot of code didn’t work. We’ve been working hard on getting this new feature implemented. Try out C++ Modules in Update 2 and you’ll find a lot more code just works.

Working with UTF-8

We’ve made it easier to work with UTF-8 and other character sets with MSVC. It’s very hard to compile code that contains non-ASCII strings cross-platform—we refer to this as the source character set. And because the compiler interprets strings differently based on what environment the program is run in, it’s difficult to write code that will be compiled on different operating systems that treat non-ASCII strings the same everywhere. We refer to this as the execution character set. We’ve added several new command-line compiler options that allow you to specify explicitly the source and execution character sets. Because UTF-8 is the a commonly encountered character set with cross-platform code, we’ve also introduced an option that sets both the source and execution character sets to UTF-8. There’s a great blog post by Jim Springfield that explains how we’re handling character sets here: https://blogs.msdn.microsoft.com/vcblog/2016/02/22/new-options-for-managing-character-sets-in-the-microsoft-cc-compiler/

Empty base class optimization

NB: Empty base class optimization is in Update 2, not Update 2 CTP Sometimes you’ll write an empty class to be the base of a hierarchy. But that empty class isn’t really empty: C++ requires that a class have a non-zero size to ensure object identity. If, for a given class C, sizeof(C)==0, math done on a pointer to C could cause a divide by zero. (For example, how would you compute the number of elements between x and y in this expression: &a[x] - &a[y]? You’d divide the distance between the pointers by the size of the elements which cannot be zero.) Because of this, empty classes are given a minimal size. But while the size of any empty class is always non-zero, when an empty class is used as a base class it can have effectively zero size. There’s a well-known optimization called “empty base class optimization” that makes an empty base class have zero size. MSVC previously only did this optimization in a limited fashion. With Update 2 we now support it broadly. Currently you have to mark every class where you want this optimization performed with a __declspec. For example,

struct empty1 {};
struct empty2 {};
struct empty3 {};
struct __declspec(emptyBases) MyStruct : empty1, empty2, empty3
{
    int i;
    int j;
    int k;
}

We also created a compiler option, /d1reportEmptyBasesClassLayout to help identify classes where this optimization would be useful. The switch is what we call a “d1” switch, meaning that it’s a temporary and unsupported compiler switch that will be removed one day without notice. Why didn’t we just turn on empty base class optimization by default? The optimization changes class layout, meaning that it would be a binary breaking change. We try to minimize breaking changes and we know that binary breaking changes are especially painful for developers. One day we’ll be able to make this breaking change and you’ll no longer need to use the __declspec. But for now, removing the size of empty base classes can save significant binary size in some codebases so we wanted you to be able to do the optimization where appropriate in your code. You can read more about empty base class optimization in this blog post: https://blogs.msdn.microsoft.com/vcblog/2016/03/30/optimizing-the-layout-of-empty-base-classes-in-vs2015-update-2-3/

In closing

You can enjoy even more new capabilities beyond the compiler in VS2015 Update 2. We’re eager for you to try out the changes and give us feedback in the comments below or through the usual channels: email, Suggestions, Connect (for bugs), or even Send-A-Smile. NB: Edited this post on 31 Mar ’16 to reflect that Empty Base Class Optimization shipped in the full release of Update 2. Also note this post on Partial Support for Expression SFINAE in VS 2015 Update 1: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/  

0 comments

Discussion is closed.

Feedback usabilla icon