1

Is it possible to store a function that has similar behaviour to the following function:

void target(int foo, size_t bar) {}
void target(std::string foo, int bar) {} 

template<T...>
void forwarding_func(T&&... args)
{
    target(std::forward<T>(args)...);
}

auto forwarding_callable = ?

How would one build the callable object for types T that has the same behaviour as the forwarding_func. I have to store it for later use, so I do need a std::function object? Is it possible to even store lambdas like these in function objects?

auto f = [](auto&& x){
      myfunction(std::forward<decltype(x)>(x));
} 
Andreas Loanjoe
  • 2,205
  • 10
  • 26
  • yes, it's possible to use lambdas. if you want to store them, you can either use `std::function` or a class, templated on the type of a passed lambda object. – Dev Null Oct 07 '17 at 22:01
  • 1
    It is possible to store such a thing but *not* in a std::function object. – n. m. could be an AI Oct 07 '17 at 22:11
  • Please note, that your example lambda would (if it worked) NOT take a universal reference but a rvalue reference, ie. it would only accept rvalues as argumentes. I know that you're explicitly asking how to do this right, but it still seems to me that there is some confusion there. You might want to read Scott Meyer's article (https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers) on the difference between universal and rvalue references. – Knoep Oct 08 '17 at 10:00
  • "auto&& // here, “&&” does not mean rvalue reference" Doesn't he show here that auto can be deduced to a reference type aswell. – Andreas Loanjoe Oct 08 '17 at 10:55
  • Ah, I see now, where that `auto` came from... Yes, you are right, `auto&&` would be a universal reference, however (as you probably know) you can't use `auto` with function parameters. Normal functions have to specify exactly whet kind of arguments they take (and what kind of references), so there's no way to do this without a template. – Knoep Oct 08 '17 at 11:47

2 Answers2

2

You need a function object, that is, one with operator(). This operator should be a template and have the same (or similar) signature as forwarding_func. You can build one in a variety of ways.

The most straightforward way is to use C++17 generic lambda. If you can't use that, you can define your own:

struct forwarder {
    template <typename ... Args>
       void operator()(Args&& ... args) { ... etc }
};

There is a completely different way to create such forwarders. There is a proposal to add an overload function to the standard library. You would use it like this:

auto callable = std::overload (
   (void (*)(int, size_t))(target),
   (void (*)(std::string, int))(target)
);

Compilers don't implement std::overload as it isn't a part of any standard yet, but you can easily implement it yourself or find an implementation on the net (see this question for example).

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

Universal references and std::forward only make sense in function templates, as in this case you don't know whether your argument is an lvalue-reference or not. If it is, you want to pass it on as is, if not, you want to move it into the function you're forwarding to. That is exactly what std::foward does. Here's a very nice introduction to rvalue-references and perfect forwarding. As non-template functions specify whether they take their arguments by value or by (lvalue-)reference, you can just move or not move them accordingly and don't need std::foward.

As function templates are not actually functions themselves, you cannot store them in a lambda. You can however create a functor with a templated operator():

struct Forwarding_Functor
{
    template<class... T>
    void operator()(T&&... args)
    {
        target(std::forward<T>(args)...);
    }
};
Knoep
  • 858
  • 6
  • 13