GCC 和 Clang 都拒绝编译这个:
#include <string>
#include <utility>
using namespace std;
int main() {
const string s = "12345";
const string& r = s;
auto p = std::make_pair<string, string>(r, r);
}
海湾合作委员会说:
error: cannot bind rvalue reference of type ‘std::__cxx11::basic_string<char>&&’ to lvalue of type ‘const std::string’ {aka ‘const std::__cxx11::basic_string<char>’}
当 Clang 说:
error: no matching function for call to 'make_pair'
既然我给出了
make_pair
显式类型,为什么它不从 const string&
构造新的字符串?
本篇编译:
auto p = std::make_pair<string, string>(string(r), string(r));
假设 C++11 或更高版本:
std::make_pair
不应与明确指定的模板参数一起使用。它们旨在通过转发引用从函数参数中推导出来。 std::make_pair
的签名是
template<class T1, class T2>
constexpr std::pair<V1, V2> make_pair(T1&& t, T2&& u);
这表明
T1
和 T2
用作转发引用,因此不应明确指定。 (V1
/V2
由 T1
/T2
通过衰减计算。)
明确指定模板参数会破坏转发行为。使用
string
作为模板参数,您在函数参数中得到 string&&
,这是一个不接受左值的右值引用。您需要提供 const string&
作为 T
的模板参数才能使 T&&
也 const string&
.
但是不要那样做,只写
auto p = std::make_pair(r, r);
按预期用途
在 C++11 之前没有转发引用,
std::make_pair
看起来像这样:
template <class T1, class T2>
std::pair<T1, T2> make_pair(T1 t, T2 u);
所以您问题中的代码将在 C++11 之前编译。尽管如此,那时指定模板参数也是多余的。
Per pairs.spec
make_pair
的定义如下所示:
template<class T1, class T2>
constexpr pair<unwrap_ref_decay_t<T1>, unwrap_ref_decay_t<T2>> make_pair(T1&& x, T2&& y);
和回报:
pair<unwrap_ref_decay_t<T1>, unwrap_ref_decay_t<T2>>(std::forward<T1>(x), std::forward<T2>(y))
unwrap_ref_decay_t 在这里只是
std::decay_t
,因为您没有使用 reference_wrapper
(而 std::decay<string>
只是 string
)。
因此,当您显式命名模板参数时,您最终会得到这个签名:
constexpr pair<string, string> make_pair(string&& x, string&& y);
看看函数参数是如何从“转发引用”(
T&&
)变成右值引用(string&&
)的?
由于您提供了
const string&
参数,因此无法转换它们,因此编译失败。这就是为什么当你明确地通过string(r)
它开始工作。
解决方案是不传递像user17732522所说的类型参数
auto p = std::make_pair(r, r);