为什么使用 std::make_* 而不是构造函数更好?

Why is it better to use std::make_* instead of the constructor?

STL 中有一些函数以 make_ 前缀开头,例如 std::make_pairstd::make_sharedstd::make_unique 等。为什么更好的做法是使用他们而不是简单地使用构造函数 ?

auto pair2 = std::pair< int, double >( 1, 2.0 );
auto pair3 = std::make_pair( 1, 2.0 );

std::shared_ptr< int > pointer1 = std::shared_ptr< int >( new int( 10 ) );
std::shared_ptr< int > pointer2 = std::make_shared< int >( 10 );

虽然这可能是主观的,但这种技术的一个主要优势是:

write code against interfaces, not implementations

本质上,函数模板实例化根据您传递的参数执行类型推导,而 class 模板实例化则不会。因此,您不必像直接实例化 class 时那样传递模板参数。

不过应该注意,这不是关于 "saving a few characters",而是关于使您的代码更通用,并避免在您的函数调用中绑定到具体类型。

然而,情况并非总是如此,正如您的 std::make_shared 示例所示,在某些情况下您必须将类型作为模板参数传递。但是,作为 Herb Sutter points out,使用 std::make_shared 时还有其他几个优点:

  1. 你应该首先写清楚和正确,std::make_shared 实现了这两个(主观,但我同意)

  2. 使用 std::make_shared 效率更高,因为它会一次性分配您的对象以及 shared_ptr 对象,从而降低分配开销并可能实现更好的缓存对齐.

make_unique 向您隐藏了 "raw" 指针,这通常是一件好事 - 它不易出错。 make_shared 可能会改善 shared_ptr<X> 实例的内存分配。通常当你使用 shared_ptr<X> 构造函数时,它会分配内存两次,第一次是 X 的实例,第二次是它的内部数据(例如引用计数器)。 make_shared 启用优化 - 它将创建包含 X 和引用计数器的单个内部结构,因此它将执行单个内存分配。和以前一样它隐藏了原始指针。

除了启用参数推导的好处(如其他答案中已经提到的),还有一些其他好处。

std::make_pair<T1, T2> 只是 而不是 return std::pair<T1, T2>。如果您使用 std::ref 传递一个值,那么 returned 对将不会存储 std::reference_wrapper,它会存储一个引用。

std::make_shared 可以合并分配。 shared_ptr 需要一些地方来保存无法直接存储在 shared_ptr 中的引用计数、弱指针列表等内容。这些可以与正在创建的对象组合在一起,在一个稍大的块中而不是在两个单独的块中。

std::make_sharedstd::make_unique 都确保在抛出异常时不会留下任何对象。如果您将函数调用为 f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)),那么编译器可能会先分配两个 int 对象,然后构造两个 shared_ptr<int> 对象。如果 second 分配失败,并且尚未设置 shared_ptr<int> 对象以在销毁时释放内存,那么就会发生内存泄漏。 std::make_sharedstd::make_unique 在函数调用中结合了 int 的分配和 std::shared_ptr<int> 的构造,以及 int 的另一个分配和 int 的另一个构造=26=] 在另一个函数调用中。函数调用不能重叠,所以如果第二次分配失败,已经有一个共享指针将被销毁,同时撤消第一次分配。