std::make_pair类型推导原理及调用报错原因解析
先明确你的代码示例和问题:
std::vector<std::pair<int*, int*>> vec; int* a = new int(1); int* b = new int(2); vec.emplace_back(std::make_pair(a, b)); // Fine vec.emplace_back(std::make_pair<int*, int*>(&*a, &*b)); // Fine vec.emplace_back(std::make_pair<int*, int*>(a, b)); // Error
报错信息:
GCC: cannot bind rvalue reference of type ‘int*&&’ to lvalue of type ‘int*’
Clang: no matching function for call to 'make_pair'
下面分点解析差异和原因:
一、std::make_pair的默认工作逻辑
当你不显式指定模板参数时,std::make_pair会做两件事:
- 自动推导传入参数的类型;
- 对推导得到的类型应用
std::decay,最终用这个退化后的类型构造std::pair并返回。
std::decay的核心作用是把类型“标准化”为适合值传递的形式:去掉引用、cv限定符,数组转指针,函数转函数指针,确保pair存储的是可拷贝的基础值类型。
二、显式指定模板参数时的函数签名
std::make_pair的简化模板原型如下:
template <class T1, class T2> constexpr std::pair<T1, T2> make_pair(T1&& t, T2&& u);
这里的T1&&和T2&&是转发引用,但转发引用的特性仅在模板参数自动推导时生效。一旦你显式指定T1和T2的类型(比如这里的int*),T1&&就会变成普通的右值引用(即int*&&),不再具备转发左值的能力。
三、逐行代码分析
1. std::make_pair(a, b)(无显式模板参数)
编译器推导a和b的类型为int*,经过std::decay后还是int*,所以返回std::pair<int*, int*>,和vector的元素类型完全匹配。同时,因为是自动推导模板参数,转发引用会识别a、b是左值,自动推导为左值引用类型,所以可以正常绑定左值参数,编译通过。
2. std::make_pair<int*, int*>(&*a, &*b)(显式指定模板参数)
显式指定模板参数后,函数参数要求是int*&&(右值引用)。而&*a的本质是:*a是左值int,取地址操作&*a得到的是一个临时的int*表达式——这是一个右值(没有名字的临时值),右值可以直接绑定到右值引用,因此编译通过。
3. std::make_pair<int*, int*>(a, b)(显式指定模板参数)
同样显式指定了模板参数,函数参数要求是int*&&。但a和b是有名字的变量,属于左值。C++规则明确禁止将非const的右值引用绑定到左值,因此编译器直接报错,这就是你看到的错误信息的来源。
四、std::decay的通俗理解
std::decay可以看作是类型的“降维处理”:
- 对于引用类型:直接去掉引用(比如
int*&→int*); - 对于带cv限定的类型:去掉const/volatile(比如
const int*→int*); - 对于数组:转换成指向数组元素的指针(比如
int[3]→int*); - 对于函数:转换成函数指针(比如
void(int)→void(*)(int))。
它的存在是为了让std::make_pair返回的pair始终存储值类型,避免出现引用或数组等无法直接拷贝的成员。
内容的提问来源于stack exchange,提问作者Alexey104

