通常,像
{}
这样的
初始化列表不是表达式,也没有类型。如果你有一个函数模板。
template<typename T> void f(T);
如果您调用f( {} )
,则不会为T
推导类型,类型推导将失败。
另一方面,ABC{}
是一个类型为ABC
的prvalue表达式(“函数符号中的显式类型转换”)。对于像f( ABC{} )
这样的调用,函数模板可以从此表达式中推导出类型ABC
。
在C++14和C++11中,
std::pair
有以下构造函数[pairs.pair];
T1
和
T2
是
std::pair
类模板的模板参数的名称:
pair(const pair&) = default;
pair(pair&&) = default;
constexpr pair();
constexpr pair(const T1& x, const T2& y);
template<class U, class V> constexpr pair(U&& x, V&& y);
template<class U, class V> constexpr pair(const pair<U, V>& p);
template<class U, class V> constexpr pair(pair<U, V>&& p);
template <class... Args1, class... Args2>
pair(piecewise_construct_t, tuple<Args1...>, tuple<Args2...>);
注意,有一个构造函数。
constexpr pair(const T1& x, const T2& y)
但是不。
constexpr pair(T1&& x, T2&& y)
相反,有一个完全的转发。
template<class U, class V> constexpr pair(U&& x, V&& y)
如果您尝试使用至少一个初始化器为花括号初始化列表的
std::pair
进行初始化,则构造函数(P)不可行,因为它无法推断其模板参数。
(C)不是构造函数模板。其参数类型
T1 const&
和
T2 const&
由类模板参数固定。对于常量类型的引用可以从空的花括号初始化列表初始化。这将创建一个绑定到引用的临时对象。由于所引用的类型是const,因此(C)构造函数将其参数复制到类的数据成员中。
当你通过
std::pair<T,U>{ T{}, U{} }
初始化一对时,
T{}
和
U{}
是prvalue表达式。构造函数模板(P)可以推断它们的类型并且是可行的。在类型推导后产生的实例化比(C)构造函数更匹配,因为(P)将产生rvalue-reference参数并将prvalue参数绑定到它们上面。另一方面,(C)将prvalue参数绑定到lvalue-references上。
那么,为什么通过std::pair<T,U>{ {}, U{} }
调用的实时示例会移动第二个参数呢?
libstdc++定义了额外的构造函数。以下是其从78536ab78e开始的std::pair
实现的摘录,省略了函数定义、一些注释和SFINAE。 _T1
和 _T2
是 std::pair
类模板的模板参数的名称。
_GLIBCXX_CONSTEXPR pair();
_GLIBCXX_CONSTEXPR pair(const _T1& __a, const _T2& __b);
template<class _U1, class _U2>
constexpr pair(const pair<_U1, _U2>& __p);
constexpr pair(const pair&) = default;
constexpr pair(pair&&) = default;
template<class _U1>
constexpr pair(_U1&& __x, const _T2& __y);
template<class _U2>
constexpr pair(const _T1& __x, _U2&& __y);
template<class _U1, class _U2>
constexpr pair(_U1&& __x, _U2&& __y);
template<class _U1, class _U2>
constexpr pair(pair<_U1, _U2>&& __p);
template<typename... _Args1, typename... _Args2>
pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
请注意(E)构造函数模板:它将复制第一个参数并完美地转发第二个参数。对于像
std::pair<T,U>{ {}, U{} }
这样的初始化,它是可行的,因为它只需要从第二个参数中推导出一种类型。与第二个参数相比,它也更匹配(C),因此总体上更好。
“DR 811”注释在libstdc++源代码中。它指的是
LWG DR 811,它添加了一些SFINAE,但没有新的构造函数。
构造函数(E)和(X)是libstdc++的扩展。虽然我不确定它是否符合标准。
另一方面,libc++没有这些额外的构造函数。对于例子
std::pair<T,U>{ {}, U{} }
,它将
复制第二个参数。
使用两个库实现的在线演示
{}
推断出std::string
,那么如何为myPair{{}, ABC{}};
调用选择完美转发构造函数呢? - Marc Andresonstd::pair<std::unique_ptr<int>, int *> p(std::unique_ptr<int>(), 0);
这样的东西。请参见 PR 40925。 - T.C.