Понимание 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 8

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 - не выводимый контекст.