3

Attempting to create a vector of shared_ptr to int.

Where am I going wrong? Thanks. Keith :^)

#include <iostream>
#include <vector>
#include <memory>

int main() {
    std::vector<std::shared_ptr<int> > w;
    std::vector<std::shared_ptr<int> >::iterator it_w;
    w.push_back(new int(7));

    std::cout << std::endl;
}

Compiler result:

pickledegg> g++ -std=c++11 -o shared_ptr shared_ptr.cpp
shared_ptr.cpp:29:4: error: no matching member function for call to 'push_back'
        w.push_back(new int(7));
        ~~^~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:697:36: note: 
      candidate function not viable: no known conversion from 'int *' to 'const value_type' (aka
      'const std::__1::shared_ptr<int>') for 1st argument
    _LIBCPP_INLINE_VISIBILITY void push_back(const_reference __x);
                                   ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:699:36: note: 
      candidate function not viable: no known conversion from 'int *' to 'value_type' (aka
      'std::__1::shared_ptr<int>') for 1st argument
    _LIBCPP_INLINE_VISIBILITY void push_back(value_type&& __x);
                                   ^
1 error generated.
kmiklas
  • 11,204
  • 17
  • 55
  • 84

1 Answers1

9

The std::shared_ptr<T> constructor that accepts raw pointers is marked explicit. source. You will not be able to call push_back with a raw pointer, as it's not implicitly convertible to std::shared_ptr<int> which is what push_back takes as an argument. The solution is to either use emplace_back instead of push_back or to use std::make_shared<int>.

emplace_back matches the parameters given to one of T's constructors.

w.emplace_back(new int(7));

std::make_shared<int> returns an object of type std::shared_ptr<int>, avoiding this problem.

w.push_back(std::make_shared<int>(7));

You may combine these solutions.

#include <iostream>
#include <vector>
#include <memory>

int main(int argc, char** argv) {
    std::vector<std::shared_ptr<int> > w;
    std::vector<std::shared_ptr<int> >::iterator it_w;
    w.emplace_back(std::make_shared<int>(7));

    std::cout << std::endl;
}

Edit: As an additional note, always prefer std::make_shared<T>(...) over std::shared_ptr<T>(new T(...)). It is designed to avoid very subtle potential memory leaks. It also elegantly avoids the situation where you have new without delete which may bother some people.

Edit 2: Additionally, std::make_shared<T>(...) has a performance advantage where it avoids an additional allocation in `std::shared_ptr(new T(...))' see this answer.

Community
  • 1
  • 1
François Andrieux
  • 24,129
  • 6
  • 46
  • 72
  • 1
    "It also elegantly avoids the situation where you have `new` without `delete` which may bother some people" I find that it tends to bother static code analysis tools more often than people these days. – YoungJohn Jan 10 '17 at 21:03
  • 2
    `emplace_back(std::make_shared(7))` calls copy-ctor of `std::shared_ptr`, which is bit clupsy, because emplace_back is there for being able to get around that. – Zereges Jan 10 '17 at 21:03
  • Are both 'make_shared' and 'emplace_back' necessary? Is ``w.push_back(std::make_shared(7));`` acceptable? – kmiklas Jan 10 '17 at 21:07
  • Edit 3: for the ultra subtle: using `make_shared` has the downside that memory used for the actual object will not be reclaimed as long as any weak pointers are alive. But it's incredibly rare that this actually matters. – Nir Friedman Jan 10 '17 at 21:10
  • @kmiklas I believe both forms are equivalent in this case. There are other questions on SO that investigate more thoroughly the difference between the two methods ([like this one](http://stackoverflow.com/questions/4303513/push-back-vs-emplace-back) and [this one](http://stackoverflow.com/questions/22080290/are-there-any-cases-where-it-is-incorrect-to-replace-push-back-with-emplace-back)). – François Andrieux Jan 10 '17 at 21:15