2009-05-26 10:01 阿菜 编译
摘要:Visual Studio 2010 Beta 1中的VC++对C++0x提供了很多支持,很多标准也与C++0x接轨。这样做的好处是很多的,但同时也会导致以前一些不符合C++0x规范的代码无法像原来那样工作。以下是VC++小组的相关问题报告。
标签:
【51CTO独家报道】。这期间,Visual C++小组一直在测试VC++在VS 2010 Beta 1下运行的情况(或者叫做VC10 Beta 1)。以下是VC++小组的类库开发者Stephan T. Lavavej带来的几个可能导致原有代码崩溃的问题报告。
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.
(VS2010 Beta 1现在可以下载了。我最近写了些博文,关于Visual C++在VS 2010 Beta 1版中对5个C++0x核心语言:,auto,static_assert,rvalue references以及decltype的支持情况。我之后会用VC10 Beta 1这个词来说明。……今天我将讨论一下一些有可能会导致现有代码崩溃的几个STL变动。在你使用C++0x开发之前可能会想要了解一下。)
问题1:error C3861: 'back_inserter': identifier not found
在VC9 SP1下这是没问题的:
C:/Temp﹥type back_inserter.cpp
VC10 Beta 1下就有问题了:
C:/Temp﹥cl /EHsc /nologo /W4
出了什么问题?
解决方法:#include ﹤iterator﹥
The problem was that back_inserter() was used without including ﹤iterator﹥. 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 ﹤algorithm﹥ dragged in ﹤iterator﹥. In VC10 Beta 1, ﹤algorithm﹥ doesn't drag in ﹤iterator﹥.
(问题在于,back_inserter()在没有include ﹤iterator﹥的情况下被实用。C++标准库的headers会通过某种未定义的方式将其他headers包括进来。……VC9 SP1下,include﹤algorithm﹥顺带的就包括了﹤iterator﹥,但在VC10 Beta 1下需要单独写明。)
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 ﹤iterator﹥ is overwhelmingly the most popular header that people have forgotten to include.
注意:Range Insertion和Range Construction
By the way, when seq is a vector, deque, or list, instead of writing this:
(当seq是一个vector,deque或list的时候,不要这样写:)
copy(first, last, back_inserter(seq)); // 这是不好的!
而应该这样写:
seq.insert(seq.end(), first, last); // Range Insertion - 好的写法
如果只是在创建seq,只要这样写就可以了:
vector﹤T﹥ seq(first, last); // Range Construction - 好的写法
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.
问题2: error C2664: 'std::vector﹤_Ty﹥::_Inside' : cannot convert parameter 1 from 'IUnknown **' to 'const ATL::CComPtr﹤T﹥ *'
在VC9 SP1下这是没问题的:
C:/Temp﹥type vector_ccomptr.cpp
VC10 Beta 1下就有问题了:
C:/Temp﹥cl /EHsc /nologo /W4 vector_ccomptr.cpp
出了什么问题?
解决方法:使用CAdapt
The Standard containers prohibit their elements from overloading the address-of operator. CComPtr overloads the address-of operator. Therefore, vector﹤CComPtr﹤T﹥﹥ 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 ﹤atlcomcli.h﹥'s CAdapt, whose only purpose in life is to wrap address-of-overloading types for consumption by Standard containers. vector﹤CAdapt﹤CComPtr﹤T﹥﹥﹥ 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.
(标准容器禁止其元素将运算符的address-of过载。CComPtr将运算符的address-of过载,导致 vector﹤CComPtr﹤T﹥﹥被禁止。这是因为,在VC9 SP1中,vector并在push_back()中使用不使用运算符的address-of,但在VC10 Beta 1中是使用的。
解决方法就是使用﹤atlcomcli.h﹥的CAdapt。……)
问题3: error C2662: 'NamedNumber::change_name' : cannot convert 'this' pointer from 'const NamedNumber' to 'NamedNumber &'
在VC9 SP1下这是没问题的:
VC10 Beta 1下就有问题了:
出了什么问题?
解决方法:Respect set Immutability (尊重set的不变性)
The problem is modifying set/multiset elements.
(问题出在set/multiset元素的修改上。)
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).
(C++0x决定,修改set/multiset元素是危险的和不正确的。……)
VC10 Beta 1 enforces the C++0x rules.
(VC10 Beta 1遵循C++0x的规则。)
There are many alternatives to modifying set/multiset elements.
(以下是修改set/multiset元素的一些代替的方法。)
◆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﹤shared_ptr﹤T﹥, 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.
问题4:Specializing stdext::hash_compare
If you've used the non-Standard ﹤hash_set﹥ or ﹤hash_map﹥ and specialized stdext::hash_compare for your own types, this won't work anymore, because we've moved it to namespace std. ﹤hash_set﹥ and ﹤hash_map﹥ are still non-Standard, but putting them in namespace stdext wasn't accomplishing very much.
(如果你为你的type使用非标准话的 ﹤hash_set﹥或﹤hash_map﹥,以及特别的stdext::hash_compare,那你会发现这里没法儿用了,因为它们被移到了命名空间下。 ……)
解决方法:使用 ﹤unordered_set﹥ 或﹤unordered_map﹥
TR1/C++0x ﹤unordered_set﹥ and ﹤unordered_map﹥ are powered by the same machinery as ﹤hash_set﹥ and ﹤hash_map﹥, but the unordered containers have a superior modern interface. In particular, providing hash and equality functors is easier.
If you still want to use ﹤hash_set﹥ and ﹤hash_map﹥, 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
内容来源:Visual C++ 小组博客