2

I'm trying to pass a pointer to a stack variable to a function (I don't control) that only takes a boost::shared_ptr.

According to this answer, using boost::make_shared is the way to go. In order to test out this functionality I wrote this:

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

int main(int argc, char const *argv[])
{
    int i = 10;
    boost::shared_ptr<int> int_ptr = boost::make_shared(i); // doesn't work
    some_function(int_ptr); // this function takes only shared_ptr
    return 0;
}

But it throws the following error:

error: no matching function for call to ‘make_shared(int&)’
boost::shared_ptr<int> int_ptr = boost::make_shared(i);
                                                     ^

If I add the template argument like so it works but what is the reason for this?

boost::shared_ptr<int> int_ptr = boost::make_shared<int>(i);

Thank you!

Community
  • 1
  • 1
simplename
  • 717
  • 7
  • 15
  • 4
    Ask yourself what type of shared pointer should the function if you just give it an `int`. How does it know you want a `shared_ptr` and not a `shared_ptr>` which can also be constructed by an `int`. – NathanOliver Mar 17 '17 at 18:02
  • Note that template argument deduction cannot include information about the type the result would be assigned to. The `boost::shared_ptr int_ptr =` part can't be taken into account to determine the appropriate template arguments. – François Andrieux Mar 17 '17 at 18:04
  • 2
    Making a `shared_ptr` that points to stack-allocated memory would be disastrous when it tries to delete that memory. Does `i` need to be modifiable through the pointer, or will a copy of `i` suffice? – chris Mar 17 '17 at 18:04
  • @chris `i` doesn't need to be modifiable and a copy will suffice but I thought that's what the whole point of using `make_shared` was. Because the function that I have to send `int_ptr` to, doesn't accept anything other than a `shared_ptr` I have no other option. – simplename Mar 17 '17 at 18:09
  • @simplename, Well, the question specified a pointer to a stack variable. This isn't necessarily the point of `make_shared`. It avoids `new` in your code, it does optimizations on dynamic allocation, and it prevents the possibility of exceptions leaking an object in cases such as `f(new A, new A)`. – chris Mar 17 '17 at 19:02

2 Answers2

3

Given the boost::make_shared<T> template:

namespace boost {
    template<typename T, typename Arg1>
      shared_ptr<T> make_shared( Arg1 const & arg1 );
}

The template mechanism can deduce the type of parameter arg1. Because it "sees" the type of the argument i (which is int). However, it can't deduce the return type T. It does not know the type T of the boost::shared_ptr<T> you will assign to (i.e. it has no means to know the type of int_ptr.)

The boost::shared_ptr<T> uses different types for argument (Arg1) and return (T) to allow you to build shared pointers from arguments different from the pointer type. For instance, double to int:

double d = 10.0;
std::shared_ptr<int> int_ptr = std::make_shared<int>(d);

If you want to build shared pointers which the type is the same as the argument, you can write a wrapper:

template<typename T>
boost::shared_ptr<T> my_make_shared(T const & arg) {
    return boost::make_shared<T>(arg);
}

But bear in mind that while this works:

int i = 10.0;
std::shared_ptr<int> int_ptr = my_make_shared(i); // OK

The implicit type conversion does not:

double d = 10.0;
std::shared_ptr<int> int_ptr = my_make_shared(d); // ERROR

Hope it helps!

1

While Guilherme Ferreira's answer elaborates on template argument deduction (and is correct in that regard), I believe this is not the answer you're looking for.

I'm trying to pass a pointer to a stack variable to a function (I don't control) that only takes a boost::shared_ptr.

shared_ptr means shared ownership over the pointed object. If the function you're trying to call saves the pointer in some of its data structures, like a container, and then returns, the pointer will become dangling as soon as the value on the stack is destroyed, despite the fact that shared_ptr still holds a reference. In order to do what you want, you have to be absolutely sure that the function does not save the pointer anywhere and can only use it during this one call.

Provided that this condition is fulfilled, you can create a shared_ptr pointing to a value on the stack, but you can't use make_shared for that. make_shared allocates a new object on heap, along with a reference counter for it, and initializes it with the arguments you passed to the function call. The returned shared_ptr points to that new object and not to the object on the stack.

void foo()
{
    int n = 10;
    boost::shared_ptr< int > pn = boost::make_shared< int >(n);

    assert(*pn == 10); // succeeds
    assert(pn.get() == &n); // fails

    bar(pn);
}

This implies that modifications bar makes to the pointed int are not reflected on n.

In order to create a shared_ptr to an existing object, you have to use its constructor directly. Also, since the object's lifetime is controlled by the stack, you have to prohibit shared_ptr from destroying the object. This can be done by specifying a no-op deleter on shared_ptr construction.

void foo()
{
    int n = 10;
    boost::shared_ptr< int > pn(&n, boost::null_deleter());

    assert(*pn == 10); // succeeds
    assert(pn.get() == &n); // succeeds

    bar(pn);
}

Note, however, that this code still allocates heap memory for the reference counter shared_ptr uses, so you're not winning any performance.

Andrey Semashev
  • 10,046
  • 1
  • 17
  • 27