C++ reinterpret_cast of std::shared_ptr 参考优化
C++ reinterpret_cast of std::shared_ptr reference to optimize
您有两个 classes Animal
和 Dog
(其中 Dog
继承自 Animal
),并且您经常遇到这样的情况期待一只动物,但正在发送一只狗的实例。在我的特殊情况下,我经常将强指针 (std::shared_ptr<Dog>
) 投射到动物预期函数 (std::shared_ptr<Animal>
)。
如果我们接受我们可以使函数参数成为一个引用(std::shared_ptr<Animal>&
,避免争论为什么你不应该有强指针作为引用参数,因为担心改变线程的所有权),我假设我们在内存方面使用 reinterpret_cast<std::shared_ptr<Animal>&>(dog)
来转换 std::shared_ptr<Dog> dog
是安全的,对吧?
如果是这样,除了线程问题之外还会出现什么问题;比如引用计数类的?
需要明确的是,我们的目的是要有一个可以在许多情况下使用的解决方案,其中强制转换一次并不是真正可行的解决方案。更重要的是,有许多对象必须投射。此外,忽略 std::unique_ptr
可能是也可能不是更好的解决方案。
添加最后一个要求 - 在通用序列化器 class 函数是虚拟的情况下,使用普通指针不允许我更改原始 std::shared_ptr
,因此无法进行模板化。
I am often casting a strong pointer (std::shared_ptr<Dog>
) to an animal-expecting function (std::shared_ptr<Animal>
).
无需转换(即显式转换)。派生类型的共享指针可隐式转换 (1) 为基 class.
的共享指针
(a) If we accept that we can make the function parameter a reference
只有在满足一些要求的情况下,我们才能接受这个假设。首先,函数不得将引用的参数存储在函数范围之外(复制除外)。其次,作为引用传递的指针必须是本地指针或临时指针 - 如果不是,则该函数不得调用任何可以直接或间接访问引用指针的函数。
还要考虑如果引用是非常量,则不能依赖隐式转换 (1)。相反,您必须创建一个单独的正确类型的共享指针(您可以使用隐式转换来创建它并传递它。const 引用参数或非引用参数没有这个问题。
(b) I assume we would be safe memory-wise to cast a std::shared_ptr dog using reinterpret_cast<std::shared_ptr<Animal>&>(dog)
, right?
在标准方面,您建议的转换并不安全 - 它具有未定义的行为。我看不出假设 (b) 是如何从假设 (a) 推导出来的。
如果在你的情况下使用对 shared_ptr 的引用作为参数是可以的,但是由于转换(派生到基数,非 const 到 const)你无法避免引用计数器的 increment/decrement ,我建议改用裸指针作为参数。
因为无法在 std::shared_ptr
上直接使用 static_cast
、const_cast
、dynamic_cast
和 reinterpret_cast
来检索与指针共享所有权的指针作为参数传递时,应使用 std::static_pointer_cast
、std::const_pointer_cast
、std::dynamic_pointer_cast
和 std::reinterpret_pointer_cast
函数。
std::reinterpret_pointer_cast
在 C++11 和 C++14 中不可用,因为它仅由 N3920 and adopted into Library Fundamentals TS in February 2014 提出。不过可以这样实现:
template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }
您有两个 classes Animal
和 Dog
(其中 Dog
继承自 Animal
),并且您经常遇到这样的情况期待一只动物,但正在发送一只狗的实例。在我的特殊情况下,我经常将强指针 (std::shared_ptr<Dog>
) 投射到动物预期函数 (std::shared_ptr<Animal>
)。
如果我们接受我们可以使函数参数成为一个引用(std::shared_ptr<Animal>&
,避免争论为什么你不应该有强指针作为引用参数,因为担心改变线程的所有权),我假设我们在内存方面使用 reinterpret_cast<std::shared_ptr<Animal>&>(dog)
来转换 std::shared_ptr<Dog> dog
是安全的,对吧?
如果是这样,除了线程问题之外还会出现什么问题;比如引用计数类的?
需要明确的是,我们的目的是要有一个可以在许多情况下使用的解决方案,其中强制转换一次并不是真正可行的解决方案。更重要的是,有许多对象必须投射。此外,忽略 std::unique_ptr
可能是也可能不是更好的解决方案。
添加最后一个要求 - 在通用序列化器 class 函数是虚拟的情况下,使用普通指针不允许我更改原始 std::shared_ptr
,因此无法进行模板化。
I am often casting a strong pointer (
std::shared_ptr<Dog>
) to an animal-expecting function (std::shared_ptr<Animal>
).
无需转换(即显式转换)。派生类型的共享指针可隐式转换 (1) 为基 class.
的共享指针(a) If we accept that we can make the function parameter a reference
只有在满足一些要求的情况下,我们才能接受这个假设。首先,函数不得将引用的参数存储在函数范围之外(复制除外)。其次,作为引用传递的指针必须是本地指针或临时指针 - 如果不是,则该函数不得调用任何可以直接或间接访问引用指针的函数。
还要考虑如果引用是非常量,则不能依赖隐式转换 (1)。相反,您必须创建一个单独的正确类型的共享指针(您可以使用隐式转换来创建它并传递它。const 引用参数或非引用参数没有这个问题。
(b) I assume we would be safe memory-wise to cast a std::shared_ptr dog using
reinterpret_cast<std::shared_ptr<Animal>&>(dog)
, right?
在标准方面,您建议的转换并不安全 - 它具有未定义的行为。我看不出假设 (b) 是如何从假设 (a) 推导出来的。
如果在你的情况下使用对 shared_ptr 的引用作为参数是可以的,但是由于转换(派生到基数,非 const 到 const)你无法避免引用计数器的 increment/decrement ,我建议改用裸指针作为参数。
因为无法在 std::shared_ptr
上直接使用 static_cast
、const_cast
、dynamic_cast
和 reinterpret_cast
来检索与指针共享所有权的指针作为参数传递时,应使用 std::static_pointer_cast
、std::const_pointer_cast
、std::dynamic_pointer_cast
和 std::reinterpret_pointer_cast
函数。
std::reinterpret_pointer_cast
在 C++11 和 C++14 中不可用,因为它仅由 N3920 and adopted into Library Fundamentals TS in February 2014 提出。不过可以这样实现:
template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }