I was implementing a simple smart pointer class, and I decided I wanted to make my own versions of the std::make_unique
/ std::make_shared
functions to go along with it once I finished. I created these two overloads:
// note: Box<T> is my "unique pointer" type, it has a partial specialization for T[],
// and it works as expected when created outside of these functions
template <class T, class... Args> Box<T> make_box(Args &&... args) {
auto ptr = new T(std::forward<Args>(args)...);
return Box<T>(std::move(ptr));
}
template <class T> Box<T> make_box(std::size_t size) {
auto ptr = new std::remove_extent_t<T>[size];
return Box<T>(std::move(ptr));
}
The first overload works just fine, at least in this example:
struct Point3D {
double x, y, z;
Point3D() = default;
Point3D(double x, double y, double z) : x{x}, y{y}, z{z} {};
};
// works exactly as expected, Box is created and does what it's supposed to
auto box = make_box<Point3D>(1.0, 2.0, 3.0);
However, the overload for arrays doesn't seem to be used. If I try to call it with an array type as T, the program won't compile. The following code gives me an error while attempting to use the first overload, and doesn't even attempt to use the second one:
// Gives an error about "allocation of incomplete type 'Point3D []'
// from inside a template instantiation of 'make_box<Point3D [], int>'.
// the overload with one template parameter isn't used
auto box = make_box<Point3D[]>(20);
// Note that this works fine, and uses the Box specialization that calls delete[]:
Box<Point3D[]> boxed(new Point3D[20]);
What is the reason for this? The two overloads seem effectively identical to the implementations of std::make_unique
inside LLVM's libc++
and GNU's libstdc++
. It does it on multiple compilers as well (tested with GCC 10.1 and Clang 10.0.1, both compiled with -std=c++17 -Wall -Wextra -pedantic
).
Edit: Definition(s) for the Box class:
template <class T> class Box {
T *m_ptr;
public:
explicit Box(T *&&ptr) : m_ptr{ptr} {}
Box() = delete;
Box(const Box &) = delete;
Box(Box &&other) : m_ptr{other.m_ptr} {}
~Box() { delete m_ptr; }
T &operator*() const { return *m_ptr; }
T *operator->() const { return m_ptr; }
};
template <class T> class Box<T[]> {
T *m_ptr;
public:
explicit Box(T *&&ptr) : m_ptr{ptr} {}
Box() = delete;
Box(const Box &) = delete;
Box(Box &&other) : m_ptr{other.m_ptr} {}
~Box() { delete[] m_ptr; }
T &operator*() const { return *m_ptr; }
T *operator->() const { return m_ptr; }
T &operator[](std::size_t idx) { return m_ptr[idx]; }
};
Aucun commentaire:
Enregistrer un commentaire