Weird ошибка компилятора: невозможно преобразовать параметр из 'int' в 'int &&'

Что здесь происходит?
Я пытаюсь создать пару 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) не было проблем.

Я немного чувствую себя немного глупым, потому что не способен тренироваться, что здесь происходит, но что я могу сказать, я просто не понимаю.

Ответ 1

Reference говорит:

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)

Обратите внимание, что тип возврата отличается. В нем также говорится:

Выведенные типы V1 и V2 являются std:: decay:: type и std:: decay:: type (обычные преобразования типов, применяемые к аргументам функций, переданных по значению), если приложение std:: decay не приводит к std:: reference_wrapper для некоторого типа X, и в этом случае выводимый тип - X &.

Итак, с 2008 года (я имею в виду Visual С++ 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 &&, См. этот ответ, почему эта проблема.

Ответ 2

make_pair<T1,T2> не создает пару типа pair<T1,T2>, а выводит из своих аргументов подходящую пару ссылочных типов, чтобы обеспечить идеальную пересылку. Он указан как

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

для некоторых подходящих ссылочных типов V1 и V2. Это работает только в том случае, если типы аргументов выводятся, поэтому && может при необходимости распадаться на ссылку lvalue. Явным образом задавая параметры шаблона, они больше не выводятся, поэтому аргументы функции могут быть только значениями.

Решение состоит в том, чтобы позволить компилятору вывести типы:

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));   

Ответ 3

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")));

Ответ 4

Теперь std::make_pair определяется в стандарте С++

template <class T1, class T2>

см. ниже make_pair(**T1&&, T2&&**);

Вы можете написать проще, не используя std::make_pair

num_text.push_back( { 42, text } );.