Visual Studio 2010 Beta 1 is now available for download.  I've recently blogged about how Visual C++ in VS 2010 Beta 1, which I refer to as VC10 Beta 1, contains compiler support for five C++0x core language features: lambdas, auto, static_assert, rvalue references, and decltype.  It also contains a substantially rewritten implementation of the C++ Standard Library, supporting many C++0x standard library features.  In the near future, I'll blog about them in Part 4 and beyond of "C++0x Features in VC10", but today I'm going to talk about the STL changes that have the potential to break existing code, which you'll probably want to know about before playing with the C++0x goodies.


Problem 1: error C3861: 'back_inserter': identifier not found

This program compiles and runs cleanly with VC9 SP1:

C:\Temp>type back_inserter.cpp
#include
#include
#include
#include
using namespace std;

int square(const int n) {
    return n * n;
}

int main() {
    vector v;
    v.push_back(11);
    v.push_back(22);
    v.push_back(33);

    vector dest;

    transform(v.begin(), v.end(), back_inserter(dest), square);

    for (vector::const_iterator i = dest.begin(); i != dest.end(); ++i) {
        cout << *i << endl;
    }
}

C:\Temp>cl /EHsc /nologo /W4 back_inserter.cpp
back_inserter.cpp

C:\Temp>back_inserter
121
484
1089

But it fails to compile with VC10 Beta 1:

C:\Temp>cl /EHsc /nologo /W4 back_inserter.cpp
back_inserter.cpp
back_inserter.cpp(19) : error C3861: 'back_inserter': identifier not found

What's wrong?

Solution: #include

The problem was that back_inserter() was used without including .  The C++ Standard Library headers include one another in unspecified ways.  "Unspecified" means that the Standard allows but doesn't require any header X to include any header Y.  Furthermore, implementations (like Visual C++) aren't required to document what they do, and are allowed to change what they do from version to version (or according to the phase of the moon, or anything else).  That's what happened here.  In VC9 SP1, including dragged in .  In VC10 Beta 1, doesn't drag in .

When you use a C++ Standard Library component, you should be careful to include its header (i.e. the header that the Standard says it's supposed to live in).  This makes your code portable and immune to implementation changes like this one.

There are probably more places where headers have stopped dragging in other headers, but is overwhelmingly the most popular header that people have forgotten to include.

Note: Range Insertion and Range Construction

By the way, when seq is a vector, deque, or list, instead of writing this:

copy(first, last, back_inserter(seq)); // Bad!

You should write this:

seq.insert(seq.end(), first, last); // Range Insertion - Good!

Or, if you're constructing seq, simply write this:

vector seq(first, last); // Range Construction - Good!

They're not only slightly less typing, they're also significantly more efficient.  copy()-to-back_inserter() calls push_back() repeatedly, which can trigger multiple vector reallocations.  Given forward or better iterators, range insertion and range construction can just count how many elements you've got, and allocate enough space for all of them all at once.  This is also more efficient for deque, and you may as well do it for list too.


Problem 2: error C2664: 'std::vector<_Ty>::_Inside' : cannot convert parameter 1 from 'IUnknown **' to 'const ATL::CComPtr *'

This program compiles and runs cleanly with VC9 SP1:

C:\Temp>type vector_ccomptr.cpp
#include
#include
#include
#include
#include
using namespace std;

int main() {
    vector> v;

    v.push_back(NULL);
}

C:\Temp>cl /EHsc /nologo /W4 vector_ccomptr.cpp
vector_ccomptr.cpp

C:\Temp>vector_ccomptr

C:\Temp>

But it fails to compile with VC10 Beta 1:

C:\Temp>cl /EHsc /nologo /W4 vector_ccomptr.cpp
vector_ccomptr.cpp
C:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE\vector(623) : error C2664: 'std::vector<_Ty>::_Inside' : cannot convert parameter 1 from 'IUnknown **' to 'const ATL::CComPtr *'
        with
        [
            _Ty=ATL::CComPtr
        ]
        and
        [
            T=IUnknown
        ]
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
        C:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE\vector(622) : while compiling class template member function 'void std::vector<_Ty>::push_back(_Ty &&)'
        with
        [
            _Ty=ATL::CComPtr
        ]
        vector_ccomptr.cpp(9) : see reference to class template instantiation 'std::vector<_Ty>' being compiled
        with
        [
            _Ty=ATL::CComPtr
        ]
C:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE\vector(625) : error C2040: '-' : 'IUnknown **' differs in levels of indirection from 'ATL::CComPtr *'
        with
        [
            T=IUnknown
        ]

What's wrong?

Solution: Use CAdapt

The Standard containers prohibit their elements from overloading the address-of operator.  CComPtr overloads the address-of operator.  Therefore, vector> is forbidden (it triggers undefined behavior).  It happened to work in VC9 SP1, but it doesn't in VC10 Beta 1.  That's because vector now uses the address-of operator in push_back(), among other places.

The solution is to use 's CAdapt, whose only purpose in life is to wrap address-of-overloading types for consumption by Standard containers.  vector>> will compile just fine.  In VC10 Beta 1, I added operator->() to CAdapt, allowing v[i]->Something() to compile unchanged.  However, typically you'll have to make a few other changes when adding CAdapt to your program.  operator.() can't be overloaded, so if you're calling CComPtr's member functions like Release(), you'll need to go through CAdapt's public data member m_T .  For example, v[i].Release() needs to be transformed into v[i].m_T.Release() .  Also, if you're relying on implicit conversions, CAdapt adds an extra layer, which will interfere with them.  Therefore, you may need to explicitly convert things when pushing them back into the vector.


Problem 3: error C2662: 'NamedNumber::change_name' : cannot convert 'this' pointer from 'const NamedNumber' to 'NamedNumber &'

This program compiles and runs cleanly with VC9 SP1:

C:\Temp>type std_set.cpp
#include
#include
#include
#include
using namespace std;

class NamedNumber {
public:
    NamedNumber(const string& s, const int n)
        : m_name(s), m_num(n) { }

    bool operator<(const NamedNumber& other) const {
        return m_num < other.m_num;
    }

    string name() const {
        return m_name;
    }

    int num() const {
        return m_num;
    }

    void change_name(const string& s) {
        m_name = s;
    }

private:
    string m_name;
    int m_num;
};

void print(const set& s) {
    for (set::const_iterator i = s.begin(); i != s.end(); ++i) {
        cout << i->name() << ", " << i->num() << endl;
    }
}

int main() {
    set s;

    s.insert(NamedNumber("Hardy", 1729));
    s.insert(NamedNumber("Fermat", 65537));
    s.insert(NamedNumber("Sophie Germain", 89));

    print(s);

    cout << "--" << endl;

    set::iterator i = s.find(NamedNumber("Whatever", 1729));

    if (i == s.end()) {
        cout << "OH NO" << endl;
    } else {
        i->change_name("Ramanujan");
    }

    print(s);
}

C:\Temp>cl /EHsc /nologo /W4 std_set.cpp
std_set.cpp

C:\Temp>std_set
Sophie Germain, 89
Hardy, 1729
Fermat, 65537
--
Sophie Germain, 89
Ramanujan, 1729
Fermat, 65537

But it fails to compile with VC10 Beta 1:

C:\Temp>cl /EHsc /nologo /W4 std_set.cpp
std_set.cpp
std_set.cpp(55) : error C2662: 'NamedNumber::change_name' : cannot convert 'this' pointer from 'const NamedNumber' to 'NamedNumber &'
        Conversion loses qualifiers

What's wrong?

Solution: Respect set Immutability

The problem is modifying set/multiset elements.

In C++98/03, you could get away with modifying set/multiset elements as long as you didn't change their ordering.  (Actually changing their ordering is definitely crashtrocity, breaking the data structure's invariants.)

C++0x rightly decided that this was really dangerous and wrong.  Instead, it flat-out says that "Keys in an associative container are immutable" (N2857 23.2.4/5) and "For [set and multiset], both iterator and const_iterator are constant iterators" (/6).

VC10 Beta 1 enforces the C++0x rules.

There are many alternatives to modifying set/multiset elements.

·        You can use map/multimap, separating the immutable key and modifiable value parts.

·        You can copy, modify, erase(), and re-insert() elements.  (Keep exception safety and iterator invalidation in mind.)

·        You can use set/multiset, comparator>, being careful to preserve the ordering and proving once again that anything can be solved with an extra layer of indirection.

·        You can use mutable members (weird) or const_cast (evil), being careful to preserve the ordering.  I strongly recommend against this.

I should probably mention, before someone else discovers it, that in VC10 Beta 1 we've got a macro called _HAS_IMMUTABLE_SETS .  Defining it to 0 project-wide will prevent this C++0x rule from being enforced.  However, I should also mention that _HAS_IMMUTABLE_SETS is going to be removed after Beta 1.  You can use it as a temporary workaround, but not as a permanent one.


Problem 4: Specializing stdext::hash_compare

If you've used the non-Standard or and specialized stdext::hash_compare for your own types, this won't work anymore, because we've moved it to namespace std and are still non-Standard, but putting them in namespace stdext wasn't accomplishing very much.

Solution: Use or

TR1/C++0x and are powered by the same machinery as and , but the unordered containers have a superior modern interface.  In particular, providing hash and equality functors is easier.

If you still want to use and , you can specialize std::hash_compare, which is where it now lives.  Or you can provide your own traits class.

By the way, for those specializing TR1/C++0x components, you should be aware that they still live in std::tr1 and are dragged into std with using-declarations.  Eventually (after VC10) this will change.


This isn't an exhaustive list, but these are the most common issues that we've encountered.  Now that you know about them, your upgrading experience should be more pleasant.

Stephan T. Lavavej
Visual C++ Libraries Developer