94

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 ?

9
  • 3
    It is picking up the input iterator ctor Commented Mar 8, 2012 at 13:24
  • Very similar to stackoverflow.com/a/9504162/841108 Commented Mar 8, 2012 at 13:28
  • @PlasmaHH In my actual code I had many entries in the initializer list, so I don't believe this is the issue. Commented Mar 8, 2012 at 13:38
  • @juanchopanza: It is the issue in the code you pasted here, which you can easily see by tracing back the instantiation traces. Of course we can't say anything to code you have not presented here. Commented Mar 8, 2012 at 13:43
  • @PlasmaHH That's palusible. The code pasted is the example from the C++11 FAQ I referenced. But if I drop the unique_ptrs and use bare string pointers, the two argument initializer list works fine. Commented Mar 8, 2012 at 13:53

3 Answers 3

108

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.

Sign up to request clarification or add additional context in comments.

5 Comments

+1 for the links! And no, I am not actually concerned about making a vector of unique_ptrs to string. It is just the example from the FAQ.
The first example will not leak. The , in the initializer list is a sequence point (in the new, somewhat more cumbersome terminology, in { a, b }, a is sequenced before b). See C++11 §8.5.4/4. It's still a good idea to use make_unique, though.
Is there any reason why std::initializer_list can't have move semantics like any other object?
The question initializer_list and move semantics discusses why std::initializer_list doesn't have move semantic. The top answer links to an ISO proposal that addresses this limitation.
8

This question now has a better answer, at least in C++17 (C++11 will require a bit more effort). Since this is the first google result when I look for "initializing a vector of unique_ptr", I figured it's worth updating with a solution. Instead of using an initializer list, you can use a variadic function. It's an 8-line gnarly little utility function that looks like this:

#include<memory> #include<vector> #include<type_traits> template <class T> auto move_to_unique(T&& t) { return std::make_unique<std::remove_reference_t<T>>(std::move(t)); } template <class V, class ... Args> auto make_vector_unique(Args ... args) { std::vector<std::unique_ptr<V>> rv; (rv.push_back(move_to_unique(args)), ...); return rv; } 

We can now make a vector with an intuitive syntax:

auto vs = make_vector_unique<std::string>(std::string{"Doug"}, std::string{"Adam"}); 

You can even use it to make a vector of derived class objects.

class B {}; class D : public B {}; auto vb = make_vector_unique<B>(D{}, D{}, D{}); 

1 Comment

What is this syntax? (rv.push_back(move_to_unique(args)), ...); Does not seem to compile .
1

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!

5 Comments

Aaaand... that error message tells us nothing. Why is it not possible to use the explicit ctor?
@Xeo - which bit of that work explicitly call the ctor? There might be an implicit call there, but that's forbidden.
The semantics of a unique_ptr is that it can not be copied, thats the reason the constructor is deleted. But shouldn't std::vector be move-aware and so be able to move from a temporary to the vector?
@drhirch It cannot be copy constructed, but it can be explicitly constructed a from string. So the error is that what is being attempted here is an implicit construction from a string pointer.
@VJovic I think the error is that it is not possible to implicitly construct the unique_ptr from a string pointer, so I would say that "the error says that it is not possible to implicitly construct unique_ptr because the constructor is explicit. Would you agree?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.