【问题标题】:C++11 make_pair with specified template parameters doesn't compile具有指定模板参数的 C++11 make_pair 无法编译
【发布时间】:2012-03-27 08:53:24
【问题描述】:

我只是在启用 -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 也可以正常编译代码。

【问题讨论】:

  • 一个很好的问题。另一个 C++11 中微妙的突破性变化的例子,类似于 the breaking change in std::vector construction。至少这会产生编译器错误,而不是语义上的静默变化。
  • 如果我有一个整数变量 i。我想与 i 和另一个对象配对。我应该怎么称呼makepair。 1) make_pair 2) int&& j = i; make_pair?两者都不起作用。正确的做法是什么?

标签: c++ templates g++ c++11 rvalue-reference


【解决方案1】:

这不是std::make_pair 的用途;您不应该明确指定模板参数。

C++11 std::make_pair 接受两个参数,类型为T&amp;&amp;U&amp;&amp;,其中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 是一个左值表达式(它不是临时的,也没有被移动)。这意味着函数模板与您的参数不匹配,这就是您收到错误的原因。

那么,当您没有明确指定模板参数列表中的TU 时,为什么它会起作用?简而言之,右值引用参数在模板中是特殊的。部分由于名为reference collapsing 的语言特性,A&amp;&amp; 类型的右值引用参数(其中A 是模板类型参数)可以绑定到任何类型的A

A 是左值、右值、const-qualified、volatile-qualified 或 unqualified 无关紧要,A&amp;&amp; 可以绑定到该对象(同样,当且仅当 A本身就是一个模板参数)。

在您的示例中,我们进行调用:

make_pair(s, 7)

这里,sstd::string 类型的左值,7int 类型的右值。由于您没有为函数模板指定模板参数,因此执行模板参数推导以确定参数是什么。

要将左值s 绑定到T&amp;&amp;,编译器将T 推导出为std::string&amp;,从而产生std::string&amp; &amp;&amp; 类型的参数。但是,没有对引用的引用,所以这个“双重引用”折叠成std::string&amp;s 是匹配项。

7 绑定到U&amp;&amp; 很简单:编译器可以将U 推导出为int,产生int&amp;&amp; 类型的参数,它成功绑定到7,因为它是一个右值。

这些新的语言特性有很多微妙之处,但如果你遵循一个简单的规则,那就很容易了:

如果一个模板参数可以从函数参数中推导出来,就让它被推导出来。除非绝对必须,否则不要显式提供参数。

让编译器完成繁重的工作,无论如何,它在 99.9% 的情况下都会是您想要的。如果它不是您想要的,您通常会得到一个易于识别和修复的编译错误。

【讨论】:

  • 这是一个非常好的和全面的解释。谢谢!
  • @James - 我应该阅读另一篇文章或答案中的“一个简单规则”吗?
  • @MichaelBurr:不,我只是编造的。 :-) 所以,我希望这是真的!我认为这是真的......这条规则几乎一直对我有用。
  • @James:谢谢。它周围的“引用框”让我觉得它可能最初是在别处写的。这个答案真的很丰富,我只是想确保我没有遗漏其他地方。
  • 这是否也适用于元组?
猜你喜欢
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
相关资源
最近更新 更多