Search code examples
c++classtemplatesmemorynew-operator

Allocate memory for a new object from template argument type


I want to be able to create a new object of type T as illustrated in this code snippet.

template<typename T, typename Arg1, typename Arg2>
void ManageDevice(T& device, Arg1 arg1, Arg2 arg2)
{
    auto newDevice = new T{ arg1, arg2 };
    // ... Perform more operations on newDevice
}

I am currently using the function like this:

CertainClass* device;
ManageDevice(device, arg1, arg2);

The compiler returns the error: Cannot convert from "initializer list" to "class of T*".

I also tried:

auto newDevice = new decltype(device){ arg1, arg2 };

And got the following error:

Error C2464 'class of T*&': cannot use 'new' to allocate a reference

So I removed the reference with this:

using DeviceType = std::remove_reference<decltype(device)>::type;
auto newDevice = new DeviceType{ arg1, arg2 };

And I got the first error message again.

Questions:

  1. Any idea if this is even legal and what could I do to make it work?
  2. In an ideal world I would love to call the function by passing directly a pointer to the class instead of an instance of this class, is it possible?
  3. What would be the name of this?

Solution

  • Your factual and template parameters do not match. When the template is written as

    template<typename T>
    void foo(T& t);
    

    and called with

    D* d;
    foo(d);
    

    The type of T inside template is going to be D*. Now, if you try

    x = new D*{'a', 'b', 'c'};
    

    You will get an obvious compilation error, as there is no (pseudo) constructor for pointer which takes those arguments.

    To achieve your goals, you should use

    template<typename T, typename Arg1, typename Arg2>
    void ManageDevice(T*& device, Arg1 arg1, Arg2 arg2)...
    

    In this case, when called with

    Device* device;
    ManageDevice(device...);
    

    The actual T inside template would be deduced as Device.

    This was written in assumption that you use device as an output parameter. If I guessed, correctly, a better option is to actually return the value, rather than use output parameter:

    template<typename T, typename Arg1, typename Arg2>
    T* ManageDevice(Arg1 arg1, Arg2 arg2) {
       ...
       return new_device;
    }
    

    Then, you can call it as

    auto* device = ManageDevice<Device>(a, b);
    

    And while on it, replace T* with unique_ptr<T> and new with std::make_unique.