VS2008 SP1:将一对推入向量时没有合适的默认构造函数可用

VS2008 SP1: No appropriate default constructor available when pushing a pair into vector

背景

Class Foo 具有用户声明的构造函数,因此没有 implicitly-declared default constructor:

struct Foo {
    Foo(...) {...}
};

然后在std::pairstd::vector中使用如下:

std::vector<std::pair<std::string, Foo> >

用法

正在尝试向后推向量:

std::vector<std::pair<std::string, Foo> > v;
v.push_back(std::make_pair(std::string("some string"), Foo(...)));

编译错误(VS2008 SP1)

以下error C2512

'Foo' : no appropriate default constructor available
...\Microsoft Visual Studio 9.0\VC\include\utility(43): 
while compiling class template member function 'std::pair<_Ty1,_Ty2>::pair(void)'

备注

问题

导致错误的原因是什么? VS2008 SP1 有错误吗?如果是,解决方法是什么?

TL;DR

这是 VS 2008 SP1 中的错误。最简单的解决方法是在检测到 VS 2008 SP1 时提供默认构造函数。

说明

经过一些研究,我发现 thread on msdn forum 描述了类似的情况。该线程包含 Microsoft 员工的回答,提供了清晰的解释。

引用如下(为简洁起见,强调我的意思):

Thanks for reporting this bug ... This was introduced in the Visual C++ 2008 Feature Pack, which was incorporated into SP1.

We used OR here (and in tuple's _Move_operation_category) intentionally. We wanted to consider pair<int, string> to be fast-swappable (which it is). Unfortunately, we forgot that the Swaptimization requires a default constructor, and that pair/tuple allow user-defined types to sneak in. (With something like vector<T> or shared_ptr<T>, even if T doesn't have a default constructor, the vector or shared_ptr does.) Clearly, this was my bad.

There's a silver lining to this conformance bug: at least this error is letting you know that vector<pair<foo, wstring> > will be slower than vector<wstring>.

...

As workarounds, you can:

  1. Give foo a default constructor. This will fast-swap the wstring and general-swap the foo.
  2. Give foo a default constructor, and a swap() implementation that can be picked up through ADL. This will fast-swap both the wstring and the foo.
  3. Write your own pair. This will disable the "Swaptimization".
  4. Use vector<pair<shared_ptr<foo>, wstring> >. This will fast-swap the shared_ptr and wstring. Of course, now you're doing more dynamic memory allocations, so this is desirable only in certain circumstances.

Note that when we get move semantics, this swap machinery will be eliminated, which is going to be so awesome.

解决方法

在考虑了变通方法后,我选择了#1:如果检测到 VS2008 SP1,则提供默认构造函数:

struct Foo {
    Foo(...) {...}
#if _MSC_FULL_VER == 150030729 // Visual Studio 2008, SP1
    Foo() {} //<- work-around for VS2008 SP1 bug
#endif
};