1

Why do I get automatic template parameter deduction if I call class constructor directly but I do not get it in std::unique_ptr and std::make_unique? Here is an example:

#include <memory>
template <class T>
class C
{
public:
 C(const T * const t_) : t(t_) {}
 ~C(void) { delete t; }
private:
 const T * const t;
};

Example 1 (works):

int main(void)
{
 const int * const t = new int;
 auto * const c = new C(t);
 return 0;
}

Example 2 (does not compile):

int main(void)
{
 const int * const t = new int;
 auto * const c = new C(t);
 std::unique_ptr p(c); // class template argument deduction failed
 return 0;
}

Example 3 (works):

int main(void)
{
 const int * const t = new int;
 const auto c = std::make_unique<C<int>>(t);
 return 0;
}

Example 4 (does not compile):

int main(void)
{
 const int * const t = new int;

 // no matching function for call to ‘make_unique<template<class T> class C>(const int* const&)
 const auto c = std::make_unique<C>(t);
 return 0;
}

The code was compiled with g++ -std=c++17 (gcc-11.2.0).

What is the problem in examples 2 and 4? How can one fix them?

Thank you very much for your help!

S.V
  • 2,149
  • 2
  • 18
  • 41
  • 2
    The problem is that you are passing a `const int*` to `unique_ptr`. How is it supposed to know that you want it to convert that argument to a `C`? – super Dec 14 '21 at 19:18
  • On an unrelated note, don't use `new` and pointers everywhere in your code. Allocating on the stack and passing by reference should be your default options. – super Dec 14 '21 at 19:19
  • 1
    @super Actually, I pass `c` to `unique_ptr`. – S.V Dec 14 '21 at 19:19
  • `explicit unique_ptr(pointer p) noexcept;`. `pointer` here is `std::remove_reference::type::pointer`. So, to deduce argument, you need to know deleter type beforehand. – Osyotr Dec 14 '21 at 19:21
  • @S.V Ah, right. My bad. – super Dec 14 '21 at 19:22
  • @Genjutsu Adding an explicit destructor (`~C(void) { delete t; }`) to `C` does not solve the problem. – S.V Dec 14 '21 at 19:24
  • @S.V deleter is no the same as destructor. Please, refer to this question: https://stackoverflow.com/questions/51109767/why-cant-unique-ptrs-template-arguments-be-deduced – Osyotr Dec 14 '21 at 19:28
  • @Genjutsu So, the conclusion is that automatic template parameter deduction does not work with `unique_ptr`? Or there is a solution? – S.V Dec 14 '21 at 19:41

2 Answers2

4

In #2, CTAD is deliberately disabled for std::unique_ptr to avoid the issues with std::unique_ptr(new T[10]), which looks just like std::unique_ptr(new T) but needs delete[] instead of delete.

In #4, the language simply doesn’t support this: there’s no way to pass any class template as a template argument so as to do CTAD based on the function arguments inside std::make_unique. (One could pass a class template with a specific (meta)signature, but that wouldn’t be appropriate for a general facility.)

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • In #2: Is there any way to tell `std::unique_ptr` to use `delete` (and not `delete []`)? It sounds like it would solve the problem. In #4: the intent is to call `C` constructor on `t` (here automatic template parameter has to work) and then create a `std::unique_ptr` out of the result. So, it seems, the problem is identical here to problem in #2, and so if #2 is resolved, then #4 should be resolved as well. – S.V Dec 15 '21 at 14:05
  • 1
    @S.V: The way to say to use `delete` is your answer. `C`, not being a class, doesn’t have constructors; CTAD is *really* weird in that it examines the constructors declared for a class **template**. #4 already knows it’s a single object since it’s not “`C[]`” (which can’t ever be used); you can, as I said, write a wrapper which accepts a certain kind of class template as a template template argument and uses CTAD to select a specialization of it. – Davis Herring Dec 15 '21 at 19:44
0

This is one way to solve it:

template <class T>
auto MakeUnique(T t) 
{ return std::unique_ptr<std::remove_reference_t<decltype(*t)>>(t); }

int main(void)
{
 const int * const t = new int;
 auto * const c = new C(t);
 auto p = MakeUnique(c);
 return 0;
}
S.V
  • 2,149
  • 2
  • 18
  • 41