为什么 make_pair<string, string>() 在给定 const string& 时不调用复制构造函数?

问题描述 投票:0回答:2

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++ templates reference rvalue lvalue
2个回答
3
投票

假设 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 之前编译。尽管如此,那时指定模板参数也是多余的。


1
投票

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);
© www.soinside.com 2019 - 2024. All rights reserved.