AMA
AMA

Reputation: 4214

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

Background

Class Foo has user-declared constructor and thus no implicitly-declared default constructor:

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

It is then used in the std::vector of std::pair as follows:

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

Usage

Attempting to push back in the vector:

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

Compilation error (VS2008 SP1)

The following 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)'

Notes

Question

What is causing the error? Is there a bug in VS2008 SP1? If yes, what are the work-arounds?

Upvotes: 2

Views: 233

Answers (1)

AMA
AMA

Reputation: 4214

TL;DR

This is a bug in VS 2008 SP1. Simplest possible work-around is providing a default constructor when VS 2008 SP1 is detected.

Explanation

After doing some research I found the thread on msdn forum describing similar situation. The thread contains an answer from Microsoft employee which provides clear explanation.

Here's the quote (shortened for brevity, emphasis mine):

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.

A work-around

After considering the work-arounds I went with #1: providing a default constructor if VS2008 SP1 is detected:

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

Upvotes: 1

Related Questions