向量构造函数对:初始化列表与显式构造
Pair of vector constructors: initializer list vs explicit construction
我用两种方式调用 std::pair<vector<int>, int>
构造函数:
- 传递初始化列表
- 传入显式向量(r 值)。
出于某种原因,初始化列表版本制作了一个副本(并销毁了一个)。
这是我的最小代码示例:
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)
。
我用两种方式调用 std::pair<vector<int>, int>
构造函数:
- 传递初始化列表
- 传入显式向量(r 值)。
出于某种原因,初始化列表版本制作了一个副本(并销毁了一个)。
这是我的最小代码示例:
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)
。