3
votes

The following gives me compiler error:

could not deduce template argument for 'const std::weak_ptr<_Ty> &' from 'std::shared_ptr'

#include <memory>

class Foo
{
public:

    template<typename R>
    void Bar(std::weak_ptr<R> const & p)
    {
        p;
    }
};

int main(void)
{
    auto foo = Foo();
    auto integer = std::make_shared<int>();

    foo.Bar(integer);
}

I tried,

template<typename R>
void Bar(std::weak_ptr<R::element_type> const & p)
{

}

, which seems syntatically incorrect. The following works but I wondered if it's possible to get the conversion in p, without creating another temporary?

template<typename R>
void Bar(R const & p)
{
    auto w = std::weak_ptr<R::element_type>(p);
}

To be clear I'd like to explicitly state the function should take a shared or weak_ptr, so I don't like the R const & p solution.

For completeness this also works of course:

template<typename R>
void Bar(std::shared_ptr<R> const & p)
{
    auto w = std::weak_ptr<R>(p);
}
4
element_type is the type of shared_ptr (trait). <R> in this instance is a shared_ptr<R:element_type>. At least that seems to be what the type deduction process is telling me. So in the example element_type will be "int". - Robinson

4 Answers

3
votes

The template parameter R of std::weak<R> cannot be deduced from an instance of std::shared_ptr<A> because the converting constructor (which takes std::shared_ptr<Y>) is a constructor-template which meansY could be anything — and there is no way to deduce R from Y (which is deduced to be A). Have a look at the converting constructor.

You could write this:

template<typename T>
auto make_weak(std::shared_ptr<T> s) ->  std::weak_ptr<T>
{
  return { s };
}

Then call it as:

foo.Bar( make_weak(integer) );
2
votes

With C++ 17's class template deduction, this now should be legal:

auto shared = std::make_shared< int >( 3 );
auto weak = std::weak_ptr( shared );
std::weak_ptr weak2( shared );

See it on coliru.

1
votes

You need to handle two (language-wise) totally unrelated types, so you need to provide two overloads, one one for each pointer type. The shared_ptr version can then call through to the weak_ptr version by providing the correct T in its invocation.

1
votes

Because you are smarter than the compiler, you must help it deduce that type.

Observe this part of your code.

template<typename R>
void Bar(std::weak_ptr<R> const & p)

You know that for every possible R that could exist, there is exactly one R for which there is an implicit conversion from std::shared_ptr<int>. You likely didn't write any conversion operators elsewhere that would apply here.

Your C++ compiler won't know or assume this. So you should call the function as:

foo.Bar( std::weak_ptr<int>{integer} );

Or try the approach in Mark B's answer.