向量构造函数对:初始化列表与显式构造

Pair of vector constructors: initializer list vs explicit construction

我用两种方式调用 std::pair<vector<int>, int> 构造函数:

出于某种原因,初始化列表版本制作了一个副本(并销毁了一个)。
这是我的最小代码示例:

auto dummy() {
    return pair<vector<int>, int>{ {1,2,3,4,5}, 1};
}

auto dummy1() {
    return pair<vector<int>, int>{ vector{1,2,3,4,5}, 1};
}

auto dummy2() {
    return optional<vector<int> > { {1,2,3,4,5} };
}

查看编译器资源管理器后,我发现初始化列表dummy()版本调用了operator new两次,delete一次。这种情况既不会发生在显式构造版本 dummy1() 中,也不会发生在 dummy2() 中类似的 std::optional 构造函数中。我不希望有这种行为。有谁知道为什么?我也用 clang 检查过。

问题来自std::pair 构造函数和模板参数推导/重载解析:

pair( const T1& x, const T2& y ); // (1)

template< class U1, class U2 >
pair( U1&& x, U2&& y );           // (2)

请注意,至少有一个 "missing" 构造函数:

pair( T1&& x, T2&& y );           // (3)

当你对第一个参数使用列表初始化时,选择的构造函数不是(2)(with U1 = std::initializer_list<int>)而是(1)1.因此,您需要构造一个临时的 std::vector<int>,它作为 const 引用传递给 (1),它必须制作一个副本。

您可以通过以下任一方式凭经验确认这一点:

  • 使用上面提到的第三个构造函数创建自己的 pair — 在这种情况下,将选择 (3),并移动临时 vector
  • 在构建 std::pair 时显式构建 std::initializer_list<int>
pair<vector<int>, int>{ std::initializer_list<int>{1,2,3,4,5}, 1 };

另一方面std::optional作为一个单一的模板构造函数:

template < class U = value_type >
constexpr optional( U&& value );

...但是 U 有一个默认值,这使得此构造函数成为重载解析的有效候选者。


1 当您调用 pair{ {1,2,3,4,5}, 1 } 时,U1(2) 中的非推导上下文中 [temp.deduct.type]#5.6,所以推导失败,这就是为什么选择(1)