23 C++11 bugs you no longer need to worry about in VS2012

 

Visual Studio 2012 has fixed a lot of bugs in C++11 features which were present in Visual Studio 2010.

Of course, VS2010 was released before the C++ standard was finalised. Now that the standard is final, Visual Studio doesn’t have to follow a moving target, and its compliance with the standard is getting a lot better.

This means that you can drop a lot of the workarounds or start using features you’ve previously avoided. Your code can be cleaner and more straightforward.

Let’s have a look at some of the bugs which are now history…

Lambda bugs

Using lambdas in the previous version of Visual Studio required you to tread carefully.

1 One bug in VS2010 was to do with nested lambdas and capturing variables. Consider this code:

int power_level = 80;
vector planes;
for_each(planes.begin(), planes.end(), 
    [=](JetPlane& plane)
{
    for_each(plane.engines().begin(), plane.engines().end(), 
        [=](Engine& engine) { engine.set_power_level(power_level); });
});

If I change the capture lists from using the default mode to capturing power_level explicitly, the code will no longer compile in VS2010 because the compiler thinks power_level isn’t accessible to the inner lambda:

int power_level = 80;
vector planes;
for_each(planes.begin(), planes.end(), 
    [power_level](JetPlane& plane)
{
    for_each(plane.engines().begin(), plane.engines().end(), 
        [power_level](Engine& engine) { engine.set_power_level(power_level); });
});

The workaround is either to use a default capture mode (as in the original example), or to copy power_level into a local variable in the outer lambda and capture the copy in the inner lambda.

2 Another, more insidious problem occurred due to a combination of storing lambdas in an array and instantiating a type with a constructor in one of the lambdas. This code, for example, resulted in a crash (sometimes):

function lambda_array[] = {
    [] () -> bool { cout << "a"; return true; },     
    [] () -> bool { string s; cout << "b"; return false; }
};
 
lambda_array[0]();  // may cause a crash at runtime

Note string s constructed in the second lambda. This is what caused the crash.

3 One more problematic example was a combination of a templated class, a lambda expression and an initializer list. This code didn’t compile in VS2010:

template<typename T>
struct A
{
    A() : _f([](const T& t) {})   // error here
    {}
 
    function _f;
};

The workaround was to initialize _f in the body of the constructor instead, but it’s no longer necessary.

4 You couldn’t use a static variable in a lambda within a template function:

template<typename T> 
void f(Iter)
{
    auto lambda = []() 
    {
        static typename iterator_traits::difference_type n; 
    };
}

5 Yet another bug related to static variables also involved initializer lists:

struct Foo {
    void *ptr;
};
auto str1 = ([]() -> int { 
    static const Foo val = { nullptr }; return 0; }());  // internal compiler error
auto str2 = ([]() -> int { 
    static const struct Foo val = { 0 }; return 0; }()); // fails to compile

decltype bugs

You could expect a lot of issues with non-trivial uses of decltype. Its current implementation works a lot better.

6 decltype for member types

In addition to letting you use decltype to declare variables using the type of a previous variable, the VS2012 compiler has been fixed to allow you to do the same with class member variables:

class A
{
   int _m;
   decltype(_m) m2;
};
A a;

7 declval

declval is now included, so you have the ability to use classes with no default constructor as decltype expressions:

class A
{
private:
    A();
};
 
decltype(declval<a>()); // compiles OK

You should no longer need to roll your own declval.

8 Comma operator in decltype expressions

As you probably know, if you want decltype to yield a reference type, you need to put the argument expression in parentheses:

int a = 10;
is_reference<decltype((a))>::value;    // evaluates to true

However, before VS2012, if your expression included the comma operator, decltype failed to return a reference type. This is now fixed:

is_reference<decltype((a, a))>::value; // also evaluates to true

unique_ptr bugs

9 You could compile this code in VS2010:

unique_ptr<JetPlane> p;
JetPlane jet("Boeing 737");
p(&jet);

The last line invoked the deleter and in this instance caused a crash. The standard doesn’t define operator() for unique_ptr, however in the VS2010 implementation unique_ptr derived from empty deleters to take advantage of the empty base class optimization, thus exposing the deleter’s operator(). This is fixed in VS2012 by using private inheritance to prevent the empty deleter’s function call operator from being part of the unique_ptr interface.

tuple bugs

10 It was possible to store function pointers in a tuple, but it wasn’t possible to use make_tuple to store a reference to a function. In theory, a tuple can be constructed manually to store a function reference, but that didn’t work in VS2010:

make_tuple(&get_radar_data);           // returns tuple <RadarData (*)(int)>
tuple<void(&)(int)> tuple_func_ref(f); // supposed to compile but doesn't

11 VS2010 didn’t have the tuple_cat template to concatenate tuples, so you couldn’t do this:

auto t = tuple_cat(make_tuple(1, string("two")), make_tuple(3.0, string("four")));
get<3>(t);  // returns the string "four"

Both of these issues have been rectified.

bind bugs

12 Since lambdas are essentially function objects, it should be possible to use them with bind. However, bind relies on the result_of template to figure out the return type of function objects. VS2010 has a pre-C++11 implementation of this template which can’t evaluate the return type of lambdas, and it made lambdas impossible to use with bind.

The workaround is to wrap a lambda with std::function and then pass it to bind.

13 The VS2010 implementation of bind didn’t allow the use of move-only types, so you couldn’t write this:

unique_ptr<string> up(new string("abc"));
auto bound = bind(&string::size, move(up));
bound();

In VS2012, you can combine bind with lambdas and move-only types to your heart’s content.

emplace member functions

14 emplace()/emplace_back()/emplace_front() are intended to construct elements in place instead of moving/copying an object into the container, and they are supposed to take arguments for the element’s constructor rather than the element itself as a parameter. However, due to the lack of variadic templates, Visual Studio 2010 provided these functions with non-standard signatures, merely as synonyms to the insert and push_* operations. With Visual Studio 2012, these functions became useful (although they still support a limited number of arguments):

vector<pair<int, string>> pairs;
pairs.emplace_back(10, "ten");
pairs.emplace(pairs.begin(), 0, "zero");
 
unordered_map<int, string> hash;
hash.emplace(10, string("ten"));

to_string/to_wstring

15 to_string()/to_wstring() overloads for types other than long long, unsigned long long, and long double were missing, causing ambiguity if you tried to pass these functions an int or a double.

Type traits bugs

16 The type_traits library had quite a few issues with rvalue references.

is_rvalue_reference failed when passed either an lvalue reference or an rvalue reference to a function:

is_rvalue_reference<int (&)(int)>::value;   // true, but should be false
is_rvalue_reference<int (&&)(int)>::value;  // false, but should be true

is_reference returned the wrong value when passed an rvalue reference to a function:

is_reference<int (&&)(int)>::value;    // false, but should be true

is_function and is_object couldn’t handle rvalue references:

is_function<int&&>::value;  // doesn't compile
is_object<int&&>::value;  // doesn't compile

Other VS2010 type properties templates also had problems with rvalue reference support:

is_const<const int&&>::value;       // doesn't compile (should be false)
is_volatile<int&&>::value;          // doesn't compile (should be false)
is_volatile<volatile int&&>::value; // doesn't compile (should be true)

All of these issues have been rectified.

17 When both arguments of common_type are the same type which is char, unsigned char, short or unsigned short, the result of common_type was incorrectly evaluated to be int:

common_type<short, short>::type;    // yields int but should be short

This is no longer a problem.

In VS2010, common_type only worked with precisely 2 arithmetic types instead of 1 or more types (presumably because of the lack of variadic templates). Now it works with 7-12 types, depending on compiler settings.

18 is_trivial and is_standard_layout templates were synonyms for is_pod in VS2010, which isn’t correct, and as a result the queries in the following example produced wrong results:

struct Trivial          // trivial but not standard-layout
{ 
    int a;
private:
    int b;
};
 
struct StandardLayout   // standard-layout but not trivial
{
    int a;
    int b;
    ~StandardLayout();
};
 
is_trivial<Trivial>::value;                // false, but should be true
is_standard_layout<StandardLayout>::value; // false, but should be true

19 A lot of type property templates were missing in VS2010 and have been added to VS2012:

  • is_trivially_copyable

  • is_literal_type

  • is_constructible/is_default_constructible/is_copy_constructible/is_move_constructible

  • is_assignable/is_copy_assignable/is_move_assignable

  • is_destructible

  • is_trivially_constructible/is_trivially_default_constructible/is_trivially_copy_constructible/is_trivially_move_constructible

  • is_trivially_assignable/is_trivially_copy_assignable/is_trivially_move_assignable

  • is_trivially_destructible

  • is_nothrow_constructible/is_nothrow_default_constructible/is_nothrow_copy_constructible/is_nothrow_move_constructible

  • is_nothrow_assignable/is_nothrow_copy_assignable/is_nothrow_move_assignable

  • is_nothrow_destructible

  • has_virtual_destructor

20 The sign conversion templates weren’t very well behaved in VS2010.

make_signed lost const and volatile qualifiers in all unsigned to signed type conversions:

make_signed<const unsigned char>::type;     // type is char, const lost
make_signed<volatile unsigned int>::type;   // type is int, volatile lost
// type is int, both const & volatile lost
make_signed<const volatile unsigned long>::type;

make_signed converted some integer types incorrectly (although the result types happen to use the right number of bytes for storage):

make_signed<unsigned long>::type; // VS2010 converts to int instead of long
make_signed<signed char>::type;   // type is char, but should be signed char

Similarly, make_unsigned converted long to int, and lost const and volatile qualifiers (in signed to unsigned conversions in this case).

21 underlying_type for determining the underlying type of an enum wasn’t available.

unordered_set

22 Instead of specifying the number of buckets, you should be able to specify the expected number of elements with reserve(), however this wasn’t implemented:

known_size.reserve(100);    // compile error

make_exception_ptr

23 Instead of providing make_exception_ptr() to create an exception_ptr that refers to a copy of the function’s argument, VS2010 provided non-standard copy_exception() to perform the same job.

The logical question to ask after all this is…

Is Visual Studio 2012 C++11 support totally bug free?

The answer is no. Visual Studio 2012 still has bugs in C++11 features (luckily, most of them can be worked around). There isn’t much overlap between VS2010 and VS2012 though – the bugs are mostly different.

I’m hard at work on the VS2012 edition of C++11 Rocks. The first 13 chapters are already available. With this book, you can quickly learn all the intricate details of the C++11 features available in Visual Studio 2012 – bugs and all.

 

Keen on mastering C++11/14? I’ve written a book about the C++11/14 features. You can get a VS2013, Clang or a GCC edition.

 

One Response to “23 C++11 bugs you no longer need to worry about in VS2012”

  1. Article not only clears bugs on VS 2010 but also makes us familiar with the features of C++11.

     
    • Motiram
    • Reply

Leave a Comment