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

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

提问人:Prismatic 提问时间:12/20/2014 最后编辑:Michael GazondaPrismatic 更新时间:12/22/2014 访问量:623

问:

出于减少键入以下内容的简单原因:

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<...>;

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

C++ C++11 智能指针 using 指令

评论


答:

4赞 Michael Gazonda 12/20/2014 #1

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

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

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

使用别名声明被视为完全相同的类型。不仅“相似”,而且完全相同。这就是可以继续进行而不必担心以后任何后果的原因。

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

另一方面,要记住的另一件事是还有谁会阅读你的代码? 它是什么很清楚,但不太清楚。或者更确切地说,也许不是“不太清楚”,而是需要理解。它为坐下来查看您的代码的人增加了一个减速带,因为他们需要在内部翻译 和 和 .std::shared_ptrsptruptrsptrunique_ptrshared_ptr

引用标准 (7.1.3)

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

typedef-name 也可以由 alias-declaration 引入。这 using 关键字后面的标识符将变为 typedef-name,并且 identifier appertains 后面的可选 attribute-specifier-seq 添加到该 typedef-name。它具有相同的语义,就好像它是 由 typedef 说明符引入。特别是,它没有定义 新类型,则不得出现在类型 ID 中。

1赞 galop1n 12/20/2014 #2

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

#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

在这里,即使 和 是相同的,也是不同的。ABC<A>C<B>

-4赞 Walter 12/20/2014 #3

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

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


当然,您可以使用更具描述性的名称,例如(在评论中建议)并使用 typedefs 通过实现它,或者,如果它不可用,(尽管如果您没有,我会认为您也没有移动语义,因此整个重点都丢失了)。但这不会帮助减少打字unique_pointerstd::unique_ptrboost::unique_ptrstd::unique_ptrunique_pointer

评论

0赞 Michael Gazonda 12/20/2014
“是的,你当然可以这样做,但你应该这样做吗?”你只是给出了拒绝的理由,这是一个不平衡的论点。
0赞 Walter 12/20/2014
@MichaelGazonda一个不平衡的论点——真是胡说八道!没有充分的理由。因此,没有什么可以平衡我的论点。
1赞 stefan 12/20/2014
@Walter好吧,有一个更好的名字(比如一个更冗长的名字),我看不出有什么坏处。事实上,这实际上有一个好处:不支持的库版本是真实存在的。因此,一个简单的编译时开关允许代替。用户实际上并不需要知道它是哪个实现,只要它的行为类似于 .unique_pointerstd::unique_ptrunique_pointerboost::unique_ptrunique_pointer
0赞 Walter 12/22/2014
OP希望避免减少打字。你没有实现这个目标。unique_pointer
0赞 N0vember 3/3/2015
@Walter 减少打字和减少冗长是两回事。第二个比你选择想象的更有意义。但是,也许只有C++的年轻人才能说,指针在代码行上占用1个字符,而拥有指针在屏幕空间上占用15个字符的事实是彻头彻尾的愚蠢。简洁在一门语言中也很重要,而不仅仅是为了省去打字。是的,我在这里批评C++,尽管我每天都在使用它并且喜欢它。
0赞 stefan 12/20/2014 #4

对于 ,通过std::shared_ptr<T

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

因为根据定义,这些名称是等效的。

唯一指针是另一回事,因为您忽略了模板参数。因此,您只能获得 的默认模板参数,这限制了 over 的用例。uptruptrunique_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)

在这里,删除器的默认类型(即 )被替换以正确关闭 .这很重要,因为没有编辑。这也适用于任何 C 接口,其中您需要代替 ,因为默认的删除器 s,这与 ing 是不同的东西。std::default_delete<T>FILE*FILE*newfreedeletedeletefree

你不能支持它,因为它是 a 而不是 .uptr<T>template <typename T> using uptr<T> = ...template <typename T, typename D = std::default_deleter<T>> using uptr<T, D> = ...

如果您对使用不同的删除器不感兴趣,那么重命名为 也没有什么坏处,因为类型完全相同。unique_ptruptr