9

Why the compiler is not able to deduce the template parameter for std::forward?

I mean:

#include <memory>
#include <iostream>

struct X{};

struct A{
    A( const X& ) { std::cout << "cpy ctor\n"; }
    A( X&& ) { std::cout << "move ctor\n"; }
};

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

I know this is a design choice (due to the std::remove_reference in the definition of std::forward) to avoid the user forget to specify the type. What I can't get is: why the way it's implemented works to prevent deduction? Why the compiler is not just deducing forward's template parameter as Arg.

Paolo M
  • 12,403
  • 6
  • 52
  • 73
  • 4
    Can you clarify the question? Are you asking why the design was chosen to prevent argument deduction, or why the way it's implemented works to prevent deduction? – Angew is no longer proud of SO Aug 25 '15 at 13:12
  • 1
    The point of specifying the type manually is so that `forward` can decide if it should `move` `a` or not. Template argument deduction let you figure out the type of `a` but not if it should be moved or not. – nwp Aug 25 '15 at 13:16
  • 1
    @Angew The second one. – Paolo M Aug 25 '15 at 13:19

3 Answers3

12

std::forward is declared like so:

template< class T >
T&& forward( typename std::remove_reference<T>::type& t );

typename std::remove_reference<T>::type is a non-deduced context. The compiler has no way of knowing which T should be deduced because it doesn't understand the semantic connection between the type member type and a given T. It would need to search through all types to find a match and be able to somehow disambiguate collisions. This is unreasonable, so the standard doesn't allow it.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Yes, this is what I was asking for. I think reading [this](http://stackoverflow.com/questions/25245453/what-is-a-nondeduced-context) will do the rest. – Paolo M Aug 25 '15 at 13:27
9

The reason you have to specify a type for forward, by design, is what happens to a inside the function:

template<typename T,typename Arg>
T* factory( Arg&& a )
{
    // 'a' is always an lvalue here

Since a is always an lvalue, there isn't enough information in a itself to be able to determine if it was passed in as an lvalue or rvalue. That information is only available via the type Arg, which will be either X or X&. Without that extra type information, it's impossible to know whether or now a must be forwarded as an lvalue or rvalue... which is why you need to provide it:

    return new T(std::forward<Arg>(a));
}
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 4
    Well, I thought OP was asking a slightly different question. Will leave this here anyway. – Barry Aug 25 '15 at 13:23
  • Yes, my bad. It was not very clear from the question. I hope the edit helps to figure out which was my trouble. – Paolo M Aug 25 '15 at 13:25
0

From C++11 standard:

14.8.2.5 Deducing template arguments from a type

The non-deduced contexts are:

— The nested-name-specifier of a type that was specified using a qualified-id

— The expression of a decltype-specifier.

— A non-type template argument or an array bound in which a subexpression references a template parameter.

— A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.

etc...

std::forward is declared like this:

template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept

According to first sentence above: typename std::remove_reference<_Tp>::type is non deduced context.

Elohim Meth
  • 1,777
  • 9
  • 13