Mayank Jain
Mayank Jain

Reputation: 2564

Perfect Forwarding not working in C++

I am trying to understand the concept of forwarding in C++ and wrote below code to understand this functionality on variadic templates.

#include<utility>
#include<string>
#include<tuple>
#include<sstream>

template<typename... Args> struct Helper_Class
{
    std::tuple<Args...> argTuple;
    Helper_Class(Args&&... args):
                    argTuple(std::make_tuple(std::forward<Args>(args)...))
    {}
};

template<typename... Args>  std::ostream& 
operator<< ( std::ostream& os,Helper_Class<Args...> obj)
{
    return os;  
}

template<typename...Args>
Helper_Class< Args...> 
Print(Args&&... args)
{
    return Helper_Class<Args...>(std::forward<Args>(args)...);
}

template <typename... Ts>
void test( Ts &&...params) {
    std::stringstream s;
    s <<Print(std::forward<Ts>(params)...);
}

int main()
{
    test(1,2,"foo", 'x');
}

However, while executing the code I am getting below error:

error: no matching function for call to ‘std::tuple<int, int, const char (&)[4], char>::tuple(std::tuple<int, int, const char*, char>)’
      argTuple(std::make_tuple(std::forward<Args>(args)...))

What I am not understanding is if I am trying to do perfect forwarding (means types should not change between function calls) why is it trying to change the type?

Please excuse if this is very basic as I am learning this concept for very first time.

Update 1: Continuation question posted on:Question

Upvotes: 3

Views: 747

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118330

template <typename... Ts>
void test( Ts &&...params) {
    std::stringstream s;
    s <<Print(std::forward<Ts>(params)...);
}

You're going to start here. The template parameters to Print will come out of std::forward. As such, they will already be forwarded lvalue/rvalue references, et al. That's what you are going to shove here, as parameters to the Print template.

Then:

template<typename...Args>
Helper_Class< Args...> 
Print(Args&&... args)

This is now going to forward Args... to Helper_Class, which will use those types to try to declare a tuple containing lvalue references, rvalue references, et al. That's not going to work very well.

You need to decay these suckers:

template<typename...Args>
Helper_Class< typename std::decay<Args>::type...>
Print(Args&&... args)
{
    return Helper_Class<typename std::decay<Args>::type...>(std::forward<Args>(args)...);
}

Or, let's use C++14 to clean up some visual clutter...

template<typename...Args>
auto Print(Args&&... args)
{
    return Helper_Class<typename std::decay<Args>::type...>(std::forward<Args>(args)...);
}

This now compiled for me, with gcc 6.2.1.

P.S. This is not exactly "basic"...

Upvotes: 5

Related Questions