模板别名shared_ptr和unique_ptr时是否存在任何问题或限制

Are there any gotchas or limitations when template aliasing shared_ptr and unique_ptr?

本文关键字:ptr 任何 存在 问题 是否 unique 别名 shared      更新时间:2023-10-16

为了减少键入以下内容的简单原因:

std::shared_ptr<...>;
std::unique_ptr<...>;

每次我想使用智能指针时,我都会考虑使用模板别名:

template <typename T> using sptr = std::shared_ptr<T>;
template <typename T> using uptr = std::unique_ptr<T>;

所以我可以像这样使用它们:

sptr<...>;
uptr<...>;

假设我在自己的命名空间中保护它们,那么以这种方式使用带有shared/unique_ptr的模板别名是否有任何困难或限制?我会不能用直接模板语法做一些我不能用别名做的事情?出于其他原因,这是个坏主意吗?

这很好,不会对代码产生不可预见的后果。

在您的示例中,std::unique_ptr<T>std::shared_ptr<T>将与uptr<T>sptr<T>相同。你甚至可以用一个静态断言来验证这一点:

static_assert(std::is_same<std::shared_ptr<T>, sptr<T>>::value, "");

使用别名声明被认为是完全相同的类型不仅"相似",而且完全相同。这就是为什么可以继续进行这项工作,而不必担心以后会有任何后果

您的别名声明将来唯一会限制的是将其他参数传递给类型以外的智能指针的能力,例如std::unique_ptr可以接受deleter。如果你愿意,你可以修改你的using声明,用一个可选的deleter参数来包含一个更完整的定义,尽管我预计大多数人永远不会使用这个功能。

另一方面,需要记住的另一件事是,还有谁会阅读您的代码?std::shared_ptr是清楚和明显的,但sptr不太清楚。或者更确切地说,也许不是"不太清楚",但它需要被理解。它为坐下来查看代码的人增加了一个减速带,因为他们需要将uptrsptr内部转换为unique_ptrshared_ptr

引用标准(7.1.3)

用typedef说明符声明的名称将成为typedef名称。在其声明的范围内,typedef名称在语法上是等效于关键字,并命名与标识符,如第8条所述因此typedef名称就是另一种类型的同义词。typedef名称不会引入新类型类声明(9.1)或枚举声明的方式

typedef名称也可以通过别名声明引入。这个using关键字后面的标识符变为typedef名称标识符appentains后面的可选属性说明符seq到typedef名称它的语义与由typedef说明符引入。特别是,它没有定义一个新的类型,它不应该出现在类型id中。

答案已经给出,在您的情况下,答案是一样的,但模板别名有时可能真的会引入新名称,这里是一个示例

#include <type_traits>
template <typename T> class A {};
template <typename T> using B = A<T>;
template < template <typename T> class S> class C {};
static_assert( std::is_same< A<int>, B<int> >::value, "same :)"); // they are the same
static_assert( std::is_same< C<A>, C<B> >::value, "not the same :("); // error, not the same

这里,即使AB相同,C<A>C<B>也不同。

对于std::shared_ptr<T,通过为其提供不同的名称永远不会有任何问题

template <typename T>
using sptr = std::shared_ptr<T>;

因为这些名称在定义上是等价的。

唯一指针是另一回事,因为您忽略了模板参数。因此,您只能获得uptr的默认模板参数,这限制了uptr的使用情况而不是unique_ptr

考虑一下这个小代码:

std::unique_ptr<FILE, void (*)(FILE*)> file(fopen("myfile.txt", "r"), [](FILE* f)
{
   fclose(f);
});
// use file here to perform input operations
// automatic call to fclose at the end of scope (or any scope exit)

这里,删除程序的默认类型(即std::default_delete<T>)被替换,以正确关闭FILE*。这一点很重要,因为FILE*不是new。这也适用于任何C接口,在那里你需要free而不是delete,因为默认的deleterdeletes,这与freeing不同。

您的uptr<T>无法支持,因为它是template <typename T> using uptr<T> = ...而不是template <typename T, typename D = std::default_deleter<T>> using uptr<T, D> = ...

如果您从未对拥有不同的deleter感兴趣,那么将unique_ptr重命名为uptr也没有害处,因为类型完全相同。

不要这样做。是的,你当然可以这样做,但你应该这样做吗?IHMO,它会降低代码的可读性,因为每个人都知道std::unique_ptr<>是什么,但没有人知道uptr<>是什么,除了谁写了那个奇怪的代码。。。

维护良好的代码易于阅读,使用类型和值的名称来表达它们的目的和意图,因此只需要最少的注释(避免注释,因为它们很难维护)。有了你较短的uptr<>,你就要离开这条智慧之路了。。。


当然,您可以使用更具描述性的名称,如unique_pointer(在注释中建议),并使用typedefs通过std::unique_ptrboost::unique_ptr(如果没有std::unique_ptr,我会认为您也没有移动语义,因此unique_pointer的全部意义就没了)来实现它。但这对减少键入没有帮助。

相关文章: