Initializing container of unique_ptrs from initializer list fails with GCC 4.7

C++Compiler ErrorsInitializationC++11

C++ Problem Overview


I am trying to initialise an std::vector<std::unique_ptr<std::string>> in a way that is equivalent to an example from Bjarne Stroustrup's C++11 FAQ:

using namespace std;
vector<unique_ptr<string>> vs { new string{"Doug"}, new string{"Adams"} }; // fails
unique_ptr<string> ps { new string{"42"} }; // OK

I can see no reason why this syntax should fail. Is there something wrong with this way of initializing the container?
The compiler error message is huge; the relevant segment I find is below:

> /usr/lib/gcc-snapshot/lib/gcc/i686-linux-gnu/4.7.0/../../../../include/c++/4.7.0 > /bits/stl_construct.h:77:7: error: no matching function for call to > 'std::unique_ptr<std::basic_string<char> >::unique_ptr(std::basic_string<char>&)'

What is the way to fix this error ?

C++ Solutions


Solution 1 - C++

unique_ptr's constructor is explicit. So you can't create one implicitly with from new string{"foo"}. It needs to be something like unique_ptr<string>{ new string{"foo"} }.

Which leads us to this

// not good
vector<unique_ptr<string>> vs {
    unique_ptr<string>{ new string{"Doug"} },
    unique_ptr<string>{ new string{"Adams"} }
};

However it may leak if one of the constructors fails. It's safer to use make_unique:

// does not work
vector<unique_ptr<string>> vs {
     make_unique<string>("Doug"),
     make_unique<string>("Adams")
};

But... initializer_lists always perform copies, and unique_ptrs are not copyable. This is something really annoying about initializer lists. You can hack around it, or fallback to initialization with calls to emplace_back.

If you're actually managing strings with smart pointers and it's not just for the example, then you can do even better: just make a vector<string>. The std::string already handles the resources it uses.

Solution 2 - C++

After "fixing" your example:

#include <vector>
#include <memory>
#include <string>

int main()
{
	std::vector<std::unique_ptr<std::string>> vs = { { new std::string{"Doug"} }, { new std::string{"Adams"} } }; // fails
	std::unique_ptr<std::string> ps { new std::string{"42"} }; // OK
}

I got very a clear error message:

error: converting to 'std::unique_ptr<std::basic_string<char> >' from initializer list would use explicit constructor 'std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = std::basic_string<char>, _Dp = std::default_delete<std::basic_string<char> >, std::unique_ptr<_Tp, _Dp>::pointer = std::basic_string<char>*]'

This error tells us that it is not possible to use the unique_ptr's explicit contructor!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjuanchopanzaView Question on Stackoverflow
Solution 1 - C++R. Martinho FernandesView Answer on Stackoverflow
Solution 2 - C++BЈовићView Answer on Stackoverflow