奇怪的编译器错误:无法将参数从“int”转换为“int&&”。

24

这到底是怎么了?
我试图创建一个 int 和一个 string 的对,如果我使用 "魔法值" 就可以创建对,但好像无法传入变量。

std::vector<std::pair<int, std::string> > num_text;

std::string text = "Smeg";
int num = 42;

// Works fine
num_text.push_back(std::make_pair<int, std::string>(42, std::string("Smeg")));  

// Cannot convert parameter 2 from 'std::string' to 'std::string &&'
num_text.push_back(std::make_pair<int, std::string>(42, text));

// Cannot convert parameter 1 from 'int' to 'int &&'
num_text.push_back(std::make_pair<int, std::string>(num, std::string("Smeg")));

// Cannot convert parameter 1 from 'int' to 'int &&'
num_text.push_back(std::make_pair<int, std::string>(num, text));

// Works fine again
num_text.push_back(std::make_pair<int, std::string>(42, std::string("Smeg")));

我正在使用VS 2012,并粘贴了一些在VS 2008中编写的代码。虽然我无法想象这会有任何关系,但原始(2008)代码没有问题。

我感觉有点傻,因为我无法弄清楚这里发生了什么,但我能说什么呢,我就是不明白。

4个回答

26

参考文献显示:

template< class T1, class T2 >
std::pair<T1,T2> make_pair( T1 t, T2 u );           (until C++11)

template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );       (since C++11)
请注意返回类型已经改变。它也说明:
“根据值传递的函数参数应用std::decay得出的推断类型V1和V2是std::decay :: type和std::decay :: type(应用于函数参数的通常类型转换),除非std::decay的应用导致某个类型X的std :: reference_wrapper,此时推导类型为X&。”
事实上,自2008年以来(我指Visual C ++ 2008),函数make_pair的语义已经发生了变化。您可以从std :: make_pair中删除模板参数并让其推断类型,或者如果需要创建特定类型的对,则使用std :: pair的构造函数:
num_text.push_back(std::make_pair(num, text));               // deduced type
num_text.push_back(std::pair<int, std::string>(num, text));  // specific type

编译错误的原因是您指定了类型为int(作为T1)和std::string(作为T2),因此该函数期望T1 &&T2 &&。请参见此答案,了解为什么会出现问题。


或者传递复制构造的临时对象。例如 num_text.push_back(std::make_pair<int, std::string>(42, std::string(text))) 或者省略模板参数。 - Nikos C.
@NikosC,当然不包括参数,但你的第一个解决方案是必须的。我不会发送42而不是num - Shahbaz
1
就像我之前所说的,复制构造临时对象。为了传递 num,你可以使用 num_text.push_back(std::make_pair<int, std::string>(int(num), std::string(text))) - Nikos C.
@sehe,没什么。OP问的是代码是否能在VC++ 2008中运行,所以我说“since then…”即“自2008年以来…”。顺便说一下,我不认为我提到了模板参数应该提供。不过还是谢谢你提供的链接。 - Shahbaz
@sehe,你是在说上面代码中的“或者简单地...”吗?它使用了std::pair。但你是对的。我会澄清这两者之间的区别。 - Shahbaz
显示剩余4条评论

17

make_pair<T1,T2> 并不会创建一个类型为pair<T1,T2>的pair,而是通过其参数来推导适当的引用类型对以实现完美转发。它被规定为

make_pair<T1, T2>(T1&&, T2&&) -> pair<T1,T2>

template <class T1, class T2>
pair<V1, V2> make_pair(T1&& x, T2&& y);

对于一些合适的引用类型V1V2,这只有在参数类型被推导的情况下才有效,因此&&只有在必要时才可以衰减为一个左值引用。通过显式指定模板参数,它们不再被推导,因此函数参数只能是右值。

解决方案是让编译器推导类型:

num_text.push_back(std::make_pair(42, std::string("Smeg")));  // Works fine
num_text.push_back(std::make_pair(42, text));                 // Works fine
num_text.push_back(std::make_pair(num, std::string("Smeg"))); // Works fine
num_text.push_back(std::make_pair(num, text));                // Works fine
num_text.push_back(std::make_pair(42, std::string("Smeg")));  // Works fine again

如果您需要创建特定类型的一对,不要使用 make_pair,直接创建一对。

// Works, but perhaps with more copying than you want.
num_text.push_back(std::pair<int, std::string>(num, text));   

6

make_pair通常在不明确指定模板参数的情况下使用。这是它应该被使用的方式:

num_text.push_back(std::make_pair(42, std::string("Smeg")));
num_text.push_back(std::make_pair(42, text));
num_text.push_back(std::make_pair(num, std::string("Smeg")));
num_text.push_back(std::make_pair(num, text));
num_text.push_back(std::make_pair(42, std::string("Smeg")));

或者,如果您想要精确的类型:

typedef decltype(num_text)::value_type value_type;
num_text.push_back(value_type(42, std::string("Smeg")));
num_text.push_back(value_type(42, text));
num_text.push_back(value_type(num, std::string("Smeg")));
num_text.push_back(value_type(num, text));
num_text.push_back(value_type(42, std::string("Smeg")));

2

现在,C++标准中的std::make_pair定义如下:

template <class T1, class T2>

请看下面的代码:make_pair(**T1&&, T2&&**);

你可以不使用std::make_pair,写出更简单的代码。

num_text.push_back( { 42, text } );


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接