Home > front end >  Passing unique_ptr<Derived<T>> to a function
Passing unique_ptr<Derived<T>> to a function

Time:11-16

I need to pass a unique pointer to a derived template class to a function that takes a unique base template class, like this:

template <typename T>
class Base {};

template <typename T>
class Derived : public Base<T> {};

template <typename T>
void foo(std::unique_ptr<Base<T>>){}

//or

template <typename T>
class MyClass{
public:
    MyClass(std::unique_ptr<Base<T>> arg) : _arg(std::move(arg)) {}
private:
    std::unique_ptr<Base<T>> _arg;
};

int main()
{
    auto b = make_unique<Derived<int>>();
    foo(std::move(b));
    MyClass mc(std::move(b))
}

Why is this not working and how can I fix it?

I get an error:

'void foo1<T>(std::unique_ptr<Base<T>,std::default_delete<Base<T>>>)': cannot convert argument 1 from 'std::unique_ptr<Derived<int>,std::default_delete<Derived<int>>>' to 'std::unique_ptr<Base<T>,std::default_delete<Base<T>>>'

but it work

auto derived = std::make_unique<Derived<int>>();
std::unique_ptr<Base<int>> base = std::move(derived);

CodePudding user response:

C doesn't deduce template arguments in this situation. You can specify <int>, and that will succeed.

foo<int>(std::move(b)); // fine
MyClass<int> mc(std::move(b)); // fine

See it on coliru

CodePudding user response:

You can't have template argument deduction also consider implicit conversions, at least not in most situations. Normally the argument type must match the parameter type exactly for deduction of a template argument to be possible (in this case to deduce T), but std::unique_ptr<Base<int>> and std::unique_ptr<Dervived<int>> are not the same type.

As the other answer suggests you can explicitly specify the template argument instead of trying to have it be deduced.

If you want to automate this without having to add anything to Derived or Base you can however make use of one of the exceptions to the general rule above. If the template parameter is a reference-to or pointer-to base of the argument type, then it may (with certain conditions) still be used for deduction:

// Here an exception to the deduction rules applies
// and `Base<T>*` can be deduced against a pointer `X*`
// if `X` is (uniquely) derived from a `Base<T>`
template<typename T>
auto as_base_ptr(Base<T>* p){
    return p;
}

template<typename X>
auto to_base_unique_ptr(std::unique_ptr<X> p) {
    using base_type = std::remove_pointer_t<decltype(as_base_ptr(std::declval<X*>()))>;
    return std::unique_ptr<base_type>(std::move(p));
}

template <typename T>
void foo(std::unique_ptr<Base<T>>){
}

template <typename X>
void foo(std::unique_ptr<X> p){
    foo(to_base_unqiue_ptr(std::move(p)));
}

But even simpler you can ask yourself whether you really need to have the function foo take std::unique_ptr<Base<T>> specifically (e.g. because you need access to T) or whether std::unique_ptr<X> wouldn't already be enough.

  • Related