检测std::shared_ptr是否持有原始数组(并获取其大小)



我正在开发(另一个(支持容器等标准类型的C++序列化库。我特别想支持智能指针。

C++17引入了对持有原始数组的std::shared_ptr的支持(它知道在这种情况下调用delete [](。我需要检测shared_ptr是否持有原始数组,以便我可以相应地序列化它:

template <typename T>
void serialize(Writer& writer, const std::shared_ptr<T> ptr)
{
// Writer has overloaded operator()
if (ptr)
{
if (holdsRawArray(ptr)) // How to implement this???
{
auto arrayWriter = writer.array(); // RAII
auto size = rawArraySize(ptr); // How to get this???
for (std::size_t i=0; i<size; ++i) 
arrayWriter(ptr[i]);
}
else
writer(*ptr);
}
else
writer(null);
}

如何确定C++17智能指针包含原始数组?该信息在element_type成员typedef中被擦除(通过std::remove_extent_t(。我在智能指针API中也找不到任何可以显示原始数组大小的内容。

我曾考虑在operator[]operator*上使用检测器习惯用法,但如果T是或不是原始数组,则似乎不需要实现对它们进行定义。

我尝试的是可能的吗?我希望我错过了什么,或者我可以用一些技巧。

我知道我可以强制用户使用std::shared_ptr<std::array<N,T>>std::shared_ptr<std::vector<T>>,但我只想检查一下我是否可以支持包含原始数组的智能指针。

您可以通过使用编译时类型特征检查T是否为数组类型来确定shared_ptr是否持有数组类型。它甚至在标准中实现

if constexpr (std::is_array_v<T>)

但是没有办法获得大小,因为它是动态分配的,而不是存储在任何地方。

正如Jarod42所评论的,如果使用std::shared_ptr<T[N]>(但不使用std::shared_ptr<T[]>(,我可以访问原始数组大小。

因此,我可以通过模板参数添加serialize过载来获得大小N,同时知道shared_ptr持有一个数组。

#include <iostream>
#include <memory>
#include <string>
#include <type_traits>
struct Writer // Toy example
{
template <typename T>
void operator()(const T& v) {std::cout << v << ", ";}
};
struct ArrayWriter  // Toy example
{
ArrayWriter() {std::cout << "[";}
~ArrayWriter() noexcept {std::cout << "], ";}
template <typename T>
void operator()(const T& v) {std::cout << v << ", ";}
};
// "Regular" shared_ptr
template <typename T>
void serialize(Writer& writer, const std::shared_ptr<T> ptr)
{
static_assert(!std::is_array_v<T>,
"shared_ptr<T[]> not supported: size unknowable");
if (ptr)
writer(*ptr);
else
writer("null");
}
// shared_ptr holding an array of known size
template <typename T, std::size_t N>
void serialize(Writer& writer, const std::shared_ptr<T[N]> ptr)
{
if (ptr)
{
ArrayWriter arrayWriter;
static constexpr auto size = N;
for (std::size_t i=0; i<size; ++i) 
arrayWriter(ptr[i]);
}
else
writer("null");
}
int main()
{
Writer writer;
std::shared_ptr<std::string> s{new std::string{"Hello"}};
std::shared_ptr<int[3]> n{new int[3]}; // Error prone!
std::shared_ptr<float[]> x{new float[5]}; // Size lost
n[0] = 1; n[1] = 2; n[2] = 3;
serialize(writer, s); // Outputs Hello,
serialize(writer, n); // Outputs [1, 2, 3, ],
// serialize(writer, x); // static assertion failure
return 0;
}

工作示例:https://onlinegdb.com/r1R5jv0wI

sparik的回答仍然是正确的,他说当突然给shared_ptr<T>时无法知道尺寸(我本应该把问题写得更好(。

David Schwartz在评论中指出,用户可能无法正确初始化数组元素(或使用错误的动态大小(,我的序列化程序将无法知道。即使包含原始动态数组的智能指针在我的情况下可以工作,支持它们可能也不是一个好主意。

感谢大家的评论和回答!


附录

根据这个答案,unique_ptr<T[N]>是不正规的,确实对我在GCC不起作用。

我找到了P0674R1(将make_shared扩展到支持阵列(,它提供了几个shared_ptr<T[N]>的例子,所以假设委员会在通过它时没有禁止它,这似乎是合法的。

unique_ptrshared_ptr在这方面的行为不一样,这有点烦人。

相关内容

  • 没有找到相关文章

最新更新