Я просто играл с g ++ 4.7 (один из более поздних снимков) с включенным -std = c ++ 11. Я пытался скомпилировать часть существующего кода, и один неудачный случай меня несколько смущает.
Буду признателен, если кто-нибудь сможет объяснить, что происходит.
Вот код:
#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 также без проблем компилируется с кодом.
std::vector
конструкции . По крайней мере, это приводит к ошибке компилятора, а не к тихому изменению семантики.Ответы:
Это не то, как
std::make_pair
предполагается использовать; вы не должны явно указывать аргументы шаблона.C ++ 11
std::make_pair
принимает два аргумента типаT&&
иU&&
, гдеT
иU
являются параметрами типа шаблона. Фактически это выглядит так (без учета возвращаемого типа):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);
Обратите внимание, что оба этих типа параметров являются ссылками на rvalue. Таким образом, они могут связываться только с rvalue. Это не проблема для второго переданного вами аргумента
7
, потому что это выражение rvalue.s
однако это выражение lvalue (оно не временное и не перемещается). Это означает, что шаблон функции не соответствует вашим аргументам, поэтому вы получаете сообщение об ошибке.Итак, почему это работает, если вы явно не указываете, что
T
иU
находится в списке аргументов шаблона? Короче говоря, ссылочные параметры rvalue являются специальными в шаблонах. Частично из-за языковой функции, называемой свертыванием ссылок, параметр ссылки rvalue типаA&&
, гдеA
является параметром типа шаблона, может связываться с любым типомA
.Неважно,
A
является ли это lvalue, rvalue, const-квалифицированным, изменчивым или неквалифицированным, anA&&
может связываться с этим объектом (опять же, если и только еслиA
сам является параметром шаблона).В вашем примере мы делаем звонок:
make_pair(s, 7)
Здесь
s
l-значение типаstd::string
и7
r-значение типаint
. Поскольку вы не указываете аргументы шаблона для шаблона функции, выполняется вывод аргументов шаблона, чтобы выяснить, каковы аргументы.Чтобы привязать
s
lvalue кT&&
, компилятор выводитT
значение bestd::string&
, давая аргумент типаstd::string& &&
. Однако здесь нет ссылок на ссылки, поэтому эта «двойная ссылка» сворачивается, чтобы статьstd::string&
.s
это совпадение.Это просто , чтобы связывать
7
сU&&
: компилятор может вывестиU
бытьint
, что дает параметр типаint&&
, который успешно связывается7
, потому что это Rvalue.С этими новыми языковыми функциями есть много тонкостей, но если вы будете следовать одному простому правилу, это будет довольно просто:
источник