Initialization of variables in C++ can have several forms:

C++11 introduced a generalized syntax for initialization with a braced initializer list, referred to as braced-init-list. Initialization with braced-init-list is called list initialization. There are two types of list initialization, each having multiple forms (check the above links), but simplified, we can have:

  • direct list initialization: T object {arg1, arg2, ...};
  • copy list initialization: T object = {arg1, arg2, ...};

Prior to C++17 the type for all the following objects (a, b, c and d) is deduced to std::initializer_list<int>. There is no difference between the direct-list-initialization and the copy-list-initialization on the result of the type deduction.

This, however, changed in C++17 that introduced the following rules:

  • for copy list initialization auto deduction will deduce a std::initializer_list<T> if all elements in the list have the same type, or be ill-formed.
  • for direct list initialization auto deduction will deduce a T if the list has a single element, or be ill-formed if there is more than one element.

As a result, the example above changes so that a and c are still std::initializer_list<int> but b is deduced as an int and d is ill-formed, because there is more than one value in the brace-init-list.

For more information about these changes see N3922: New Rules for auto deduction from braced-init-list.

, , , Hits for this post: 4549 .

Some of the important changs in Visual Studio 2010 in regard to VC++ are represented by the support in the C++ compiler of some of the features already approved for the new C++ standard, so far called C++0x. In this post I will give a short overview on then.

static_assert

I already wrote a post about this feature. At that time I considered it rather a niche feature. However, this looks much powerful in conjunction with the type traits classes from TR1.

static_assert checks if an expression is true at compile time. If the expression if false a custom error message is displayed and the compilation fails. If the expression is true the declaration has no effect.

In the following example I create a comparison template function, that is used later to compare values.

I want this function to be used only for integral types (the reason doesn’t matter) and I’d like the compiler to issue an error when used with any other type. Adding a static_assert check will generate a compilation error for the second call to the function, when passing strings.

auto

If you are familiar with C#, this is the C++ equivalent of var. The keyword is used to deduce the type of a declared variable from its initialization expression. The initialization expression can be an assignment, direct initialization or operator new expression. It must be noted that the auto keyword is just a placeholder, not a type, and cannot be used with sizeof or typeid.

lambda expressions

I already wrote about lambdas, but I will give a short overview again. Again, if you are familiar with C# and .NET, this is the same concept as in .NET.

A lambda functions is a function object whose type is implementation dependent; its type name is only available to the compiler. The lambda expression is composed of several parts:

  • lambda_introducer: this is the part that tells the compiler a lambda function is following. Inside the angled brackets a capture-list can be provided; this is used for capturing variables from the scope in which the lambda is created.
  • lambda-parameter-declaration: used for specifying the parameters of the lambda function.
  • lambda-return-type-clause: used for indicating the type returned by the lambda function. This is optional, because most of the time the compiler can infer the type. There are cases when this is not possible and then the type must be specified. For the example above, the return type (-> bool) is not necessary.
  • compound-statement: this is the body of the lambda.

Here [] is the lambda introducer, (int n) is the lambda parameter declaration, and {cout << n << endl;} is the lambda compound statement. There is no return type clause, because that is auto inferred by the compiler. There are cases when the compiler cannot deduce the return value and then it must be specified explicitly. A lambda expression is a syntactic shortcut for a functor. The code above is equivalent to:

Lambdas can capture variables from their scope by value, reference or both in any combination. In the example above, there was no value captured. This is a stateless lambda. On the other hand, a lambda that captures variables is said to have a state.

rvalue references

Stephan T. Lavavej wrote the ultimate guide to rvalue references. There is nothing more that can be said that is not already there. I strongly suggest you read his article to familiarize with this concept.

rvalue references are used to hold a reference to a rvalue or lvalue expression, and are introduced with &&. They enable the implementation of move semantics and perfect forwarding.

Move semantics enable transferring resources from one temporary object to another. This is possible because temporary objects (i.e. rvalues) are not referred anywhere else outside the expression in which they live. To implement move semantics you have to provide a move constructor and optionally a move assignment operator. The Standard Template Library was changed to take advantage of this feature. A classic example for the move semantics is represented by operation with sequences like vector or list. A vector allocates memory for a given number of objects. You can add elements to it and no re-allocation is done until the full capacity is reached. But when that happens, the vector has to reallocate memory. In this case it allocates a new larger chunk, copies all the existing content, and then releases the pervious memory. When an insertion operation needs to copy one element several things happen: a new element is created, its copy constructor is called, and then the old element is destroyed. With moves semantics, the allocation of a new element and its copy is no longer necessary, the existing element can be directly moved.

A second scenario where rvalue references are helpful is the perfect forwarding. The forwarding problem occurs when a generic function takes references as parameters and then needs to forward these parameters to another function. If a generic function takes a parameter of type const T& and needs to call a function that takes T&, it can’t do that. So you need an overloaded generic function. What rvalue references enable is having one single generic function that takes arbitrary arguments and then forwards them to another function.

decltype operator

This is used to yield the type of an expression. Its primary purpose is for generic programming, in conjunction with auto, for return types of generic functions where the type depends on the arguments of the function. Here are several examples:

It can be used together with auto to declare late specified return type, with the alternative function declaration syntax, which is (terms in squared brackets indicate optional parts)

In general, the expression use with decltype here should match the expression used in the return statement.

The result of the execution is:

15l
30gal
42.85l
22.64gal

When function Plus is called with arguments of the same type, the result is that type. But when the arguments differ, the resulting type is also different. In this example, when the first argument is Liters and second is Gallons, the result type must be Liters and the opposite. It is possible to do this without decltype, but the solution requires explicit specification of the resulting type.

, , , , , Hits for this post: 46133 .

The new C++0x standard provides support for type inference. The auto keyword that was doing nothing in C++ was given a new meaning: a placeholder for a type inferred by the compiler. For those familiar with C#’s var keyword, this is basically the same.

Here is a comparison between auto in C++ and var in C#.

C++ C#

with the output

C++ C#

As you can see, the use is very similar, except that in C++ you can use auto for a lambda expression, while in C# you cannot. The C# compiler raises an error, CS0815, when you try to use var with an anonymous function expressions, a lambda expression, a method group expressions, or the null literal expression, because these don’t have a type.

Just like with var, you can only use auto locally, and not as return type from a function, or parameter to a function.

While it might not be of that much help for ints or strings, using auto for instead of vector< int >::iterator, or map< string, list< string >>::const_iterator comes in handy. The auto type inference helps us write less verbose code.

In addition to auto, the C++0x standard introduces a new keyword, called decltype (think of it as ‘typeof’), that can be used to determine the type of an expression at compile time.

However, this new keyword was not yet introduced in Visual Studio 2010 CTP. According to Stephan T. Lavavej from the VC++ team, it might be possible to be introduced in a next CTP or beta version.

Implementing auto doesn’t implement decltype (C++0x’s name for what was previously called typeof) for free.

Paraphrasing our libraries and compiler front-end program manager Damien Watkins, the CTP is only the first look at our VC10 functionality and there is definitely more to come. We understand that certain features “go together”, and that adding complementary features is a good thing in general. As always, customer feedback is a vital resource in forming our plans.

, , , , , , , , Hits for this post: 61770 .