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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9641960/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:06:49  来源:igfitidea点击:

C++11 make_pair with specified template parameters doesn't compile

c++templatesg++c++11rvalue-reference

提问by vmpstr

I was just playing around with g++ 4.7 (one of the later snapshots) with -std=c++11 enabled. I tried to compile some of my existing code base and one case that failed somewhat confuses me.

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

I would appreciate if someone can explain what is going on.

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

Here's the code:

这是代码:

#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;
}

I understand that make_pair is meantto be used as the (1) case (if I specify the types, then I might as well use (3)), but I don't understand why it's failing in this case.

我知道 make_pair旨在用作 (1) 情况(如果我指定类型,那么我不妨使用 (3)),但我不明白为什么在这种情况下它会失败。

The exact error is:

确切的错误是:

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>&&'

Again, the question here is just "what's going on?" I know that I can fix the problem by removing the template specification, but I just want to know what's failing here under the covers.

同样,这里的问题只是“发生了什么?” 我知道我可以通过删除模板规范来解决这个问题,但我只想知道在幕后是什么失败了。

  • g++ 4.4 compiles this code with no problems.
  • Removing -std=c++11 also compiles with code with no problems.
  • g++ 4.4 编译此代码没有问题。
  • 删除 -std=c++11 也可以毫无问题地使用代码进行编译。

回答by James McNellis

This is not how std::make_pairis intended to be used; you are not supposed to explicitly specify the template arguments.

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

The C++11 std::make_pairtakes two arguments, of type T&&and U&&, where Tand Uare template type parameters. Effectively, it looks like this (ignoring the return type):

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

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

When you call std::make_pairand explicitly specify the template type arguments, no argument deduction takes place. Instead, the type arguments are substituted directly into the template declaration, yielding:

当您调用std::make_pair并显式指定模板类型参数时,不会发生参数推导。相反,类型参数被直接替换到模板声明中,产生:

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

Note that both of these parameter types are rvalue references. Thus, they can only bind to rvalues. This isn't a problem for the second argument that you pass, 7, because that is an rvalue expression. s, however, is an lvalue expression (it isn't a temporary and it isn't being moved). This means the function template is not a match for your arguments, which is why you get the error.

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

So, why does it work when you don't explicitly specify what Tand Uare in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called reference collapsing, an rvalue reference parameter of type A&&, where Ais a template type parameter, can bind to any kind of A.

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

It doesn't matter whether the Ais an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, an A&&can bind to that object (again, if and only if Ais itself a template parameter).

无论A是左值、右值、const 限定、volatile 限定还是非限定,A&&都可以绑定到该对象(同样,当且仅当A其本身是模板参数)。

In your example, we make the call:

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

make_pair(s, 7)

Here, sis an lvalue of type std::stringand 7is an rvalue of type int. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.

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

To bind s, an lvalue, to T&&, the compiler deduces Tto be std::string&, yielding an argument of type std::string& &&. There are no references to references, though, so this "double reference" collapses to become std::string&. sis a match.

要将s左值绑定到T&&,编译器推断Tstd::string&,从而产生类型为 的参数std::string& &&。但是,没有对引用的引用,因此这个“双重引用”折叠为std::string&. s是一场比赛。

It's simple to bind 7to U&&: the compiler can deduce Uto be int, yielding a parameter of type int&&, which binds successfully to 7because it is an rvalue.

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

There are lots of subtleties with these new language features, but if you follow one simple rule, it's pretty easy:

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

If a template argument can be deduced from the function arguments, let it be deduced. Don't explicitly provide the argument unless you absolutely must.

Let the compiler do the hard work, and 99.9% of the time it'll be exactly what you wanted anyway. When it isn't what you wanted, you'll usually get a compilation error which is easy to identify and fix.

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

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