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

时间:2014-12-19 19:52:51

标签: c++ c++11 smart-pointers using-directives

因为减少打字的简单原因:

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这样的模板别名是否存在任何问题或限制?我是否能够使用直接模板语法执行某些操作,而不能使用别名?出于其他原因,这是一个坏主意吗?

4 个答案:

答案 0 :(得分:4)

这很好,对你的代码没有任何不可预见的后果。

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

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

使用别名声明被认为是完全相同的类型。不仅仅是&#34;类似&#34;,但完全相同。这就是为什么可以继续这样做,而不用担心以后的任何后果。

您的别名声明将来唯一限制的是您能够将其他参数传递给除该类型之外的智能指针,例如: std::unique_ptr可以接受删除者。如果您愿意,可以修改您的使用声明以包含带有可选删除参数的更全面的定义,但我希望大多数人永远不会使用此功能。

另一方面,要记住的另一件事是,还有谁会阅读你的代码? std::shared_ptr明确而明显,但sptr不太明确。或者更确切地说,可能不那么清楚&#34;但是需要理解它。它为坐下来查看代码的人增加了速度,因为他们需要在内部将uptrsptr内部翻译为unique_ptrshared_ptr

引用标准(7.1.3)

  

使用typedef说明符声明的名称将成为typedef-name。   在其声明的范围内,typedef-name是语法上的   相当于一个关键字,并命名与该关联的类型   标识符以第8章中描述的方式描述。因此,typedef-name是a   另一种类型的同义词。 typedef-name不引入新类型   类声明(9.1)或枚举声明的方式

     

也可以通过别名声明引入typedef-name。该   using关键字后面的标识符变为typedef-name和   标识符appertains后面的可选attribute-specifier-seq   到那个typedef-name。它具有与它相同的语义   由typedef说明符引入。特别是,它没有定义   一个新类型,它不应出现在type-id。

答案 1 :(得分:1)

已经给出了答案,在你的情况下,它将是相同的,但模板别名有时可能真正引入新名称,这里是一个例子

#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>也不同。

答案 2 :(得分:0)

对于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。这也适用于您需要free而不是delete的任何C接口,因为默认的删除器delete,与free不同。

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

如果您从不对拥有不同的删除工具感兴趣,那么将unique_ptr重命名为uptr并不会有任何损害,因为类型完全相同。

答案 3 :(得分:-3)

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

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


当然,您可以使用更具描述性的名称,例如unique_pointer(在评论中建议),并使用typedef通过std::unique_ptr实现该名称,如果不可用,则{{1} (虽然如果你没有boost::unique_ptr我会认为你也没有移动语义,因此std::unique_ptr的整点都丢失了。但是无法帮助减少打字