As you didn't specify a c++ version, I'm gonna assume you use the latest, which now is C++17. The closest fit to your existing code is using if constexpr
, I won't elaborate on that as there are other good answers for that. If you are stuck on C++14 or C++11 (or worse 03/98, in which case you should simply upgrade), you will need to specialize your template. (I'll come back to this)
This code however, is violating one of the CppCoreGuidelines: ES.24: Use a unique_ptr<T> to hold pointers
By writing your template to detect raw pointers and delete it, one always has to allocate. Hence your linked list can't refer to some sub-data of something existing. As already eluded to in the comments, if the users want the memory to be cleaned, use std::unique_ptr
. An example:
namespace Linked{
template <class T>
struct Nodo{
T Element;
Nodo<T> *Next{nullptr};
Nodo() = default;
~Nodo() = default;
};
}
// Has ownership
auto node = Nodo<std::unique_ptr<int>>{};
node.element = std::make_unique<int>(42);
// Has ownership (to array of 42 elements)
auto node = Nodo<std::unique_ptr<int[]>>{};
node.element = std::make_unique<int[]>(42);
// No ownership
int value = 42;
auto node = Nodo<int>{};
node.element = &value;
With this, ownership is clear to the caller and transparant for you. (as you don't need to know about arrays, std::unique_ptr knows about that) You might want to put some restrictions on T, like adding static_assert(std::is_nothrow_move_constructable<T>);
.
This above solution solves the problem in C++11 and upwards and should be the recommended approach.
If not, use if constexpr
if your condition isn't capturable in a dedicated class in C++17. And partial specialization in C++14 and C++11.
namespace Linked{
template <class T>
struct Nodo{
T Element;
Nodo<T> *Next{nullptr};
Nodo() = default;
~Nodo() = default;
};
template <class T>
struct Nodo<T*>{
T *Element{nullptr};
Nodo<T> *Next{nullptr};
Nodo() = default;
~Nodo() { delete Element; }
};
}
If you don't want to repeat your code too much
namespace Linked{
template <class T, class Me>
struct AbstractNodo{
T Element;
Me *Next{nullptr};
// All common code
};
template <class T>
struct Nodo : AbstractNodo<T, Nodo<T>>{
Nodo() = default;
~Nodo() = default;
};
template <class T>
struct Nodo<T*> : AbstractNodo<T, Nodo<T*>>{
Nodo() = default;
~Nodo() { delete Element; }
};
}
There is also a way to specialize a single method, however, I'm not that familiar with it, see Stack overflow: Template specialization of a single method from a templated class for more details.