检测到 std::shared_ptr 持有原始数组(并获取其大小)
Detect that std::shared_ptr is holding a raw array (and obtaining its size)
我正在研究(另一个)支持容器等标准类型的 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[]
and operator*
上使用检测器习惯用法,但如果 T
是或不是原始数组,似乎不需要取消定义它们的实现。
我正在尝试的是可能的吗?我希望我错过了什么,或者我可以使用一些技巧。
我知道我可以强制用户改用 std::shared_ptr<std::array<N,T>>
或 std::shared_ptr<std::vector<T>>
,但我只是想检查一下我是否有可能支持保存原始数组的智能指针。
您可以通过使用编译时类型特征检查 T 是否为数组类型来确定 shared_ptr
是否包含数组类型。它甚至在 std.
中实现
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 在评论中指出,用户可能无法正确初始化数组元素(或使用错误的动态大小),而我的序列化程序无法知道。即使包含原始动态数组的智能指针在我的情况下可以工作,支持它们也可能不是一个好主意。
感谢大家的评论和回答!
附录
根据这个 answer,unique_ptr<T[N]>
格式不正确,在 GCC 中确实对我不起作用。
我找到了 P0674R1 (Extending make_shared to Support Arrays) 并且它提供了几个 shared_ptr<T[N]>
的例子,因此假设委员会在通过时没有禁止它似乎是合法的。
有点烦人的是 unique_ptr
和 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[]
and operator*
上使用检测器习惯用法,但如果 T
是或不是原始数组,似乎不需要取消定义它们的实现。
我正在尝试的是可能的吗?我希望我错过了什么,或者我可以使用一些技巧。
我知道我可以强制用户改用 std::shared_ptr<std::array<N,T>>
或 std::shared_ptr<std::vector<T>>
,但我只是想检查一下我是否有可能支持保存原始数组的智能指针。
您可以通过使用编译时类型特征检查 T 是否为数组类型来确定 shared_ptr
是否包含数组类型。它甚至在 std.
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 在评论中指出,用户可能无法正确初始化数组元素(或使用错误的动态大小),而我的序列化程序无法知道。即使包含原始动态数组的智能指针在我的情况下可以工作,支持它们也可能不是一个好主意。
感谢大家的评论和回答!
附录
根据这个 answer,unique_ptr<T[N]>
格式不正确,在 GCC 中确实对我不起作用。
我找到了 P0674R1 (Extending make_shared to Support Arrays) 并且它提供了几个 shared_ptr<T[N]>
的例子,因此假设委员会在通过时没有禁止它似乎是合法的。
有点烦人的是 unique_ptr
和 shared_ptr
在这方面的表现不一样。