具有指定模板参数的C ++ 11 make_pair无法编译


84

我只是在启用-std = c ++ 11的情况下玩g ++ 4.7(后面的快照之一)。我试图编译一些现有的代码库,而一个失败的案例使我有些困惑。

如果有人可以解释发生了什么,我将不胜感激。

这是代码:

#include <utility>
#include <iostream>
#include <vector>
#include <string>

int main ( )
{
    std::string s = "abc";

    // 1 ok
    std::pair < std::string, int > a = std::make_pair ( s, 7 );

    // 2 error on the next line
    std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );

    // 3 ok
    std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );

    return 0;
}

据我所知,make_pair是被用作(1)的情况下(如果我指定的类型,那么我还不如用(3)),但我不明白为什么它在这种情况下失败。

确切的错误是:

test.cpp: In function ‘int main()’:
    test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
    test.cpp:11:83: note: candidate is:
    In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
                 from test.cpp:1:
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’

同样,这里的问题只是“正在发生什么?” 我知道我可以通过删除模板规范来解决此问题,但是我只想知道隐藏在这里的是什么。

  • g ++ 4.4可以毫无问题地编译此代码。
  • 删除-std = c ++ 11也可以使用代码编译,没有问题。

6
一个好问题。C ++ 11中微妙的突破性变化的另一个例子,类似于构造中的突破性变化std::vector。至少这会产生一个编译器错误,而不是语义上的无声变化。
James McNellis 2012年

1
如果我有一个整数变量i。我想与我和另一个对象配对。我到底应该怎么称呼makepair。1)make_pair <* i,obj> 2)int && j = i; make_pair <j,obj>?两者都不起作用。正确的方法是什么?
PHcoDer

Answers:


135

这不是std::make_pair打算使用的方式;您不应明确指定模板参数。

C ++ 11std::make_pair接受两个参数,分别为typeT&&U&&,其中TU是模板类型参数。实际上,它看起来像这样(忽略返回类型):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

调用std::make_pair并显式指定模板类型参数时,不会进行参数推导。相反,将类型参数直接替换为模板声明,从而产生:

[return type] make_pair(std::string&& argT, int&& argU);

请注意,这两个参数类型都是右值引用。因此,它们只能绑定到右值。对于您传递的第二个参数,这不是问题7,因为这是一个右值表达式。 s,但是是一个左值表达式(它不是临时表达式,不会被移动)。这意味着函数模板与您的参数不匹配,这就是为什么会出现错误的原因。

因此,当您未明确指定模板参数列表中的内容T和内容时,为什么它起作用U?简而言之,右值引用参数在模板中是特殊的。部分归因于一种称为引用折叠的语言功能,type的右值引用参数(A&&其中A是模板类型参数)可以绑定到任何类型的A

可以绑定到该对象(同样,当且仅当它本身是模板参数时)才可以绑定到该对象A,这无关紧要。A&&A

在您的示例中,我们打电话:

make_pair(s, 7)

在这里,s是一个类型的左值std::string7类型是右值int。由于您没有为函数模板指定模板参数,因此执行模板参数推导来找出参数是什么。

要将s左值绑定到T&&编译器推导Tstd::string&,将产生type参数std::string& &&。但是,由于没有引用的引用,因此此“双重引用”折叠为std::string&s是一场比赛。

它的简单结合7U&&:编译器可以推断Uint,产生类型的参数int&&,它成功地结合7,因为它是一个rvalue。

这些新的语言功能有很多微妙之处,但是如果您遵循一条简单的规则,那就很容易了:

如果可以从函数参数中推导出模板参数,则让其推导。除非绝对必要,否则不要明确提供参数。

让编译器来做艰苦的工作,并且99.9%的时间正是您想要的。如果不是您想要的,通常会遇到易于识别和修复的编译错误。


6
这是一个很好而全面的解释。谢谢!
vmpstr 2012年

1
@James-我应该阅读另一篇文章或答案中的“一条简单规则”吗?
Michael Burr 2012年

4
@MichaelBurr:不,我刚刚做了。:-)所以,我希望这是真的!我认为这是真的……该规则几乎一直对我有效。
James McNellis 2012年

1
@詹姆斯:谢谢。它周围的“报价框”使我认为它可能是最初写在其他地方的东西。这个答案确实很有帮助,我只是想确保自己在其他地方没有错过。
Michael Burr 2012年

2
这也适用于元组吗?
Ferruccio 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.