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.

3
  • 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? Commented Aug 25, 2015 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. Commented Aug 25, 2015 at 13:16
  • 1
    @Angew The second one. Commented Aug 25, 2015 at 13:19

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, this is what I was asking for. I think reading this will do the rest.
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));
}

2 Comments

Well, I thought OP was asking a slightly different question. Will leave this here anyway.
Yes, my bad. It was not very clear from the question. I hope the edit helps to figure out which was my trouble.
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.