Понимание std:: вперед
Почему компилятор не может вывести параметр шаблона для std::forward?
Я имею в виду:
#include <memory>
#include <iostream>
struct X{};
struct A{
A( const X& ) { std::cout << "cpy ctorn"; }
A( X&& ) { std::cout << "move ctorn"; }
};
X foo() { return {}; }
template<typename T,typename Arg>
T* factory( Arg&& a )
{
return new T(std::forward(a));
// ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter
}
int main()
{
factory<A>(foo());
}
Я знаю, что это выбор дизайна (из-за std::remove_reference в определении std::forward), чтобы пользователь не забыл указать тип. Чего я не могу понять, так это: почему то, как это реализовано, работает, чтобы предотвратить дедукцию? Почему компилятор не просто выводит параметр шаблона forward как Arg.
3 ответа:
std::forwardобъявляется примерно так:template< class T > T&& forward( typename std::remove_reference<T>::type& t );
typename std::remove_reference<T>::typeявляетсянедедуцированным контекстом . Компилятор не может знать, какойTдолжен быть выведен, потому что он не понимает семантической связи между типом членаtypeи заданнымT. Он должен был бы искать во всех типах, чтобы найти совпадение и быть в состоянии как-то устранить неоднозначность столкновений. Это неразумно, поэтому стандарт не позволяет этого.
Причина, по которой вы должны указать тип для
forward, заключается в том, что происходит сaвнутри функции:template<typename T,typename Arg> T* factory( Arg&& a ) { // 'a' is always an lvalue hereС
aсоставляет всегда, в значение lvalue, у нас недостаточно информации вaсебя, чтобы иметь возможность определить, если он был принят в качестве lvalue-выражение или значение rvalue. Что информация доступна только через ТипArg, который будет либоX, либоX&. Без этой дополнительной информации о типе невозможно узнать, нужно ли пересылатьaсейчас или нет. как lvalue или rvalue... именно поэтому вам нужно предоставить его:return new T(std::forward<Arg>(a)); }
Из стандарта C++11:
14.8.2.5 выведение аргументов шаблона из типа
Недедуцированные контексты:
- вложенное-имя-описатель типа, который был определен, используя квалифицированный-идентификатор
- выражение спецификатора decltype.
- аргумент шаблона не-типа или массив, связанный в котором a подвыражение ссылается на параметр шаблона.
- параметр шаблона, используемый в параметре типа a функция параметр, который имеет аргумент по умолчанию, используемый в вызове для какого аргумента делается дедукция.
И т. д...
std::forwardобъявляется так:
template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexceptСогласно первому предложению выше:
typename std::remove_reference<_Tp>::type- не выводимый контекст.