That Guy
That Guy

Reputation: 2399

Why can't unique_ptr's template arguments be deduced?

When you have class template argument deduction available from C++17, why can't you deduce the template arguments of std::unique_ptr? For example, this gives me an error:

std::unique_ptr smp(new D);

That says "Argument list of class template is missing".

Shouldn't the template arguments (at least the pointer type) be deducable?

See this:

any declaration that specifies initialization of a variable and variable template

Upvotes: 26

Views: 3989

Answers (3)

Kai Petzke
Kai Petzke

Reputation: 2934

So this is a side effect from those olden times at the beginning of C++, when the standard makers decided to have two different delete and delete[] operators for pointers to objects and pointers to arrays of objects.

In these modern times of C++, where we have templates (they weren't there from the beginning), std::array (for fixed sized arrays), inititalizer lists (for static fixed sized arrays) and std::vector (for dynamically sized arrays), almost nobody will need the delete[] operator anymore. I have never used it, and I wouldn't be surprised, if the vast majority of the readers of this question have not used it, either.

Removing int* array = new int[5]; in favour of auto* array = new std::array<int, 5>; would simplify things and would enable safe conversion of pointers to std::unique_ptr and std::shared_ptr. But it would break old code, and so far, the C++ standard maintainers have been very keen on backwards compatibility.

Nobody stops you, though, from writing a small inlined templated wrapper function:

template<typename T>
std::unique_ptr<T> unique_obj_ptr(T* object) {
    static_assert(!std::is_pointer<T>::value, "Cannot use pointers to pointers here");
    return std::unique_ptr<T>(object);
}

Of course, you can also create a similiar function shared_obj_ptr() to create std::shared_ptrs, and if you really need them, you can also add unique_arr_ptr() and shared_arr_ptr().

Upvotes: 0

I'm not going to repeat the rationale in @NathanOliver's great answer, I'm just going to mention the how of it, the mechanics, which is what I think you are also after. You are right that if the constructor of unique_ptr looked merely like...

explicit unique_ptr( T* ) noexcept;

... it'd be possible to deduce T. The compiler generated deduction guide would work just fine. And that would be a problem, like Nathan illustrates. But the constructor is specified like this...

explicit unique_ptr( pointer p ) noexcept;

... where the alias pointer is specified as follows:

pointer : std::remove_reference<Deleter>::type::pointer if that type exists, otherwise T*. Must satisfy NullablePointer.

That specification essentially means that pointer must be an alias to __some_meta_function<T>::type. Everything on the left of ::type is a non-deduced context, which is what prevents the deduction of T from pointer. That's how these sort of deduction guides could be made to fail even if pointer needed to be T* always. Just by making it a non-deduced context will prevent the viability of any deduction guide produced from that constructor.

Upvotes: 22

NathanOliver
NathanOliver

Reputation: 180510

Lets look at new int and new int[10]. Both of those return an int*. There is no way to tell if you should have unique_ptr<int> or unique_ptr<int[]>. That right there is enough not to provide any sort of deduction guide.

Upvotes: 29

Related Questions