Meaning of std::forward<std::decay_t<F>>(f) - c++

I have a question about the code written in https://koturn.hatenablog.com/entry/2018/06/10/060000
When I pass a left value reference, if I do not remove the reference with std::decay_t, I get an error.
Here is the error message
'error: 'operator()' is not a member of 'main()::<lambda(auto:11, int)>&
I don't understand why it is necessary to exclude the left value reference.
I would like to know what this error means.
#include <iostream>
#include <utility>
template <typename F>
class
FixPoint : private F
{
public:
explicit constexpr FixPoint(F&& f) noexcept
: F{std::forward<F>(f)}
{}
template <typename... Args>
constexpr decltype(auto)
operator()(Args&&... args) const
{
return F::operator()(*this, std::forward<Args>(args)...);
}
}; // class FixPoint
namespace
{
template <typename F>
inline constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
}
} // namespace
int
main()
{
auto body = [](auto f, int n) -> int {
return n < 2 ? n : (f(n - 1) + f(n - 2));
};
auto result = makeFixPoint(body)(10);
std::cout << result << std::endl;
}

I don't understand why it is necessary to exclude the left value
reference. I would like to know what this error means.
When you pass an lvalue lambda into makeFixPoint(), the template parameter F of makeFixPoint() is instantiated as L&, where L is the lambda type. In the function body, FixPoint<std::decay_t<F>>{...} will be instantiated as FixPoint<L>{...}, so the constructor of FixPoint is instantiated as
explicit constexpr FixPoint(L&& f);
which accepts a lambda type of rvalue reference. In makeFixPoint(), if you initialize it with {std::forward<F>(f)} i.e. {std::forward<L&>(f)}, f will be forwarded as an lvalue, which will be ill-formed since rvalue reference cannot bind an lvalue.
The purpose of using {std::forward<std::decay_t<F>>(f)} is to force f to be forwarded as an rvalue.

,The code you have shared is toxic.
template <typename F>
class FixPoint : private F
{
public:
explicit constexpr FixPoint(F&& f)
what this means is that we expect F to be a value type (because inheriting from a reference isn't possible). In addition, we will only accept rvalues -- we will only move from another F.
: F{std::forward<F>(f)}
{}
this std::forward<F> is pointless; this indicates that the person writing this code thinks they are perfect forwarding: they are not. The only legal types F are value types (not references), and if F is a value type F&& is always an rvalue reference, and thus std::forward<F> is always equivalent to std::move.
There are cases where you want to
template<class X>
struct wrap1 {
X&& x;
wrap1(X&& xin):x(std::forward<X>(xin)){}
};
or even
template<class X>
struct wrap2 {
X x;
wrap2(X&& xin):x(std::forward<X>(xin)){}
};
so the above code is similar to some use cases, but it isn't one of those use cases. (The difference here is that X or X&& is the type of a member, not a base class; base classes cannot be references).
The use for wrap2 is when you want to "lifetime extend" rvalues, and simply take references to lvalues. The use for wrap1 is when you want to continue the perfect forwarding of some expression (wrap1 style objects are generally unsafe to keep around for more than a single line of code; wrap2 are safe so long as they don't outlive any lvalue passed to them).
template <typename F>
inline constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
}
Ok more red flags here. inline constexpr is a sign of nonsense; constexpr functions are always inline. There could be some compilers who treat the extra inline as meaning something, so not a guarantee of a problem.
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
std::forward is a conditional move. It moves if there is an rvalue or value type passed to it. decay is guaranteed to return a value type. So we just threw out the conditional part of the operation, and made it into a raw move.
return FixPoint<std::decay_t<F>>{std::move(f)};
this is a simpler one that has the same meaning.
At this point you'll probably see the danger:
template <typename F>
constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::move(f)};
}
we are unconditionally moving from the makeFixPoint argument. There is nothing about the name makeFixPoint that says "I move from the argument", and it accept forwarding references; so it will silently consume an lvalue and move-from it.
This is very rude.
So a sensible version of the code is:
template <typename F>
class FixPoint : private F
{
public:
template<class U,
class dU = std::decay_t<U>,
std::enable_if_t<!std::is_same_v<dU, FixPoint> && std::is_same_v<dU, F>, bool> = true
>
explicit constexpr FixPoint(U&& u) noexcept
: F{std::forward<U>(u)}
{}
[SNIP]
namespace
{
template <typename F>
constexpr FixPoint<std::decay_t<F>>
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<F>(f)};
}
} // namespace
that cleans things up a bit.

The template just can't work as intended this way.
First, because it is supposed to be possible to inherit from F, it doesn't make much sense to use std::decay_t. Instead it is probably supposed to be just std::remove_cvref_t, but before C++20 that was kind of cumbersome to write and std::decay_t does almost the same.
In the class template F is intended to be a non-reference type. It is not a template parameter of the constructor. So it cannot be used for perfect-forwarding. The constructor argument will always be a rvalue reference which cannot take lvalues.
Even if a lvalue is passed to makeFixPoint, the call std::forward<std::decay_t<F>>(f) forces conversion to a rvalue reference, which then can be used in the constructor. That is clearly dangerous. The function always behaves as if you had std::moved the argument into it.
It should just be std::forward<F>(f) and the constructor should take another template parameter that can be used to form a forwarding reference:
template<typename G>
requires std::is_same_v<std::remove_cvref_t<G>, F>
explicit constexpr FixPoint(G&& g) noexcept
: F{std::forward<G>(g)}
{}

Related

How can I construct std::tuple of references from a parameter pack?

I have a builder class that I'd like to store arguments as references for use in subsequent building.
I'd like to pass in a variable number of arguments to my class, infer the template arguments using class template argument deduction, and store those passed arguments as references in a std::tuple.
What's the easiest way to convert from a parameter pack to a std::tuple of references?
I found std::forward_as_tuple which does something similar to what I want, but I don't want a forwarding reference plus it gives a syntax error during initialization of the member tuple.
template <typename... Ts>
struct Builder
{
using ArgsT = decltype(std::forward_as_tuple(Ts{}...));
ArgsT args_;
Builder(Ts&... ts) : args_(ts...) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{foo.a, foo.b};
}
The syntax error is:
error: no matching function for call to std::tuple<int&&, double&&>::tuple(int&, double&)
If you simply wants reference, use them directly:
template <typename... Ts>
struct Builder
{
using ArgsT = std::tuple<Ts&...>;
ArgsT args_;
Builder(Ts&... ts) : args_(ts...) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{foo.a, foo.b};
}
For your code:
decltype(std::forward_as_tuple(Ts{}...)) resolves in std::tuple<Ts&&...>.
Ts{} creates a temporary (and requires your type to be default constructible).
And you cannot bind int& to int&&.
You might use decltype(std::forward_as_tuple(std::declval<Ts&>()...)) which resolves in std::tuple<Ts&...>, but later is simpler and provided solution;-).
Another way:
#include <tuple>
template<typename Tuple>
struct Builder
{
Tuple args_;
Builder(Tuple const& t) : args_(t) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{std::tie(foo.a, foo.b)};
}
Builder(Ts&... ts) is a pack of lvalue references.
Ts{}... is a pack of prvalues of the same type.
std::forward_as_tuple(Ts{}...) is a tuple containing rvalue references to the same type.
lvalue references and rvalue references are not the same thing; you cannot assign one to the other. So args_(ts...) generates the appropriate error message.
There is more than one way to produce a solution to what could be your problem.
template <typename... Ts>
struct Builder
{
using ArgsT = std::tuple<Ts&&...>; // (1)
using ArgsT = std::tuple<Ts&...>; // (2)
using ArgsT = std::tuple<Ts...>; // (3)
all 3 of these are actually reasonable ways to solve your problem, depending on later code options. Pick one depending on what your real problem is.
ArgsT args_;
What goes here:
Builder(Ts&&... ts) : args_(std::forward<Ts>(ts)...) {}
Builder(Ts&... ts) : args_(ts...) {}
Builder(Ts&&... ts) : args_(std::forward<Ts>(ts)...) {}
for each of the above 3 cases.
In case (1) you perfect forward your arguments into a tuple, and the tuple stores rvalue references to any rvalue arguments. In case (2) you only take lvalue arguments, and you store a tuple of lvalue references to them. In case (3) you perfect forward your arguments into a tuple, and store values if the argument was an rvalue and lvalue references it he value was an lvalue.
(3) is useful when you want your references to outlive the current line; the only safe way to do that is to store a copy, and then move into it.
(2) is useful if you only want to refer to lvalues.
(1) is useful if your Builder object is not going to outlive the current line, and you don't want to pay for even moving objects. It is a tad more dangerous than (1).
All 3 will make your sample code compile.

How can a class template store either reference or value?

Reading about universal references led me to wonder: how can I construct a class template such that it stores by reference if possible, or by value if it must?
That is, can I do something like this
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(T t) :obj_m { t } {}
}
auto
hold_this(T && t) { return holder<T>(t); }
Except that when hold_this() is given an lvalue the holder will hold a reference, and when given an rvalue the holder will make a copy?
Except that when hold_this() is given an lvalue the holder will hold a reference, and when given an rvalue the holder will make a copy?
You already wrote it (minus the required template <typename T>). The deduction rules for a forwarding reference preserve value category as follows:
If t is bound to an lvalue of type T2, then T = T2&.
If t is bound to an rvalue of type T2, then T = T2.
It's those deduction rules that std::forward relies on to do its job. And why we need to pass the type to it as well.
The above means that you instantiate holder directly with T2 in the rvalue case. Giving you exactly what you want. A copy is made.
As a matter of fact, two copies are made. Once to create the constructor argument t, and the other copy is to initialize obj_m from it. But we can get rid of it with some clever use of type_traits:
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(std::add_rvalue_reference_t<T> t) :obj_m { std::forward<T>(t) } {}
};
template<typename T>
auto hold_this(T && t) { return holder<T>(std::forward<T>(t)); }
See it live. We use add_rvalue_reference_t to make t be of the correct reference type in each case. And "simulate" the argument deduction which would make obj_m { std::forward<T>(t) } resolve to initializing obj_m from the correct reference type.
I say "simulate" because it's important to understand the constructor argument for holder cannot be a forwarding reference because the constructor itself is not templated.
By the way, since you tagged c++17, we can also add a deduction guide to your example. If we define it as follows (with the feedback from T.C. incorporated):
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(T&& t) :obj_m { std::forward<T>(t) } {}
};
template<typename T>
holder(T&&) -> holder<T>;
Then this live example shows you can define variables as hold h1{t}; and hold h2{test()};, with the same deduced types as the function return values from before.

Perfect forwarding with template classes

Suppose I have a template class, for example
template<int n, int m> class Matrix
Is there a way to define a matrix multiplication operator * so that
the arguments to * can be either lvalues or rvalue references
*infers the appropriate return type (i.e. the appropriate template parameters) from its arguments
What I have in mind is something like
template< int n,int k, int m, template<int,int> class T1, template<int, int> class T2, template<int,int>class T3 >
T3<n,m> operator*(T1<n,k>&&, T2<k,m>&&)//does not work
When I try to run the above code (with the bodies filled in in the obvious ways), I get an error:
Cannot convert from Matrix<1,1> to Matrix<1,1>&&
when the arguments are lvalues.
Yes. From my own code:
template
<
int LeftColumnsRightRows, int LeftRows,
int RightColumns
>
Matrix<RightColumns, LeftRows> operator*(Matrix<LeftColumnsRightRows, LeftRows> const& a, Matrix<RightColumns, LeftColumnsRightRows> const& b)
And I don't know why you'd want it to take &&s. If you want to convert two other types to matrices and then multiply them, you should do the conversion outside of the multiplication operator.
I would simply stick to const references as well, as explained by previous answer. But to clarify why your code doesn't work, perfect forwarding only applies when you use an rvalue reference to an cv-unqualified template parameter. In layman's terms, it has to be just T&&, where T is a function template parameter:
template<class T>
void ForwardMe(T&& t)
{
DoSomething(std::forward<T>(t));
}
The idea is that the compiler will be able to deduce T as type& when passed a lvalue (so the function signature becomes void ForwardMe(type&) because of reference collapsing rules), or just type in case of rvalues (signature becomes void ForwardMe(type&&)).
In your example, you do something like:
template<int N, template<int> class T>
void ForwardMe(T<N>&& t)
{
// ...
}
This doesn't work as you had expected, because the compiler cannot deduce T to be a reference to something, so you can't have perfect forwarding. The function parameter t will therefore only match rvalue references.
Since const references can bind to temporaries, using const T<N>& instead in above example will solve your problems. But if you really want to support both lvalue and rvalue inputs (because you like to have move semantics where appropriate), you have two options:
Write overloads for all 4 permutations: lvalue*lvalue, lvalue*rvalue, rvalue*lvalue, rvalue*rvalue.
Write a generic function template and use SFINAE to limit the input types.
The latter would be something like:
#include <type_traits>
template<class L, class R>
struct MatrixMulResult_helper;
template<int n, int m, int k, template<int, int> class T>
struct MatrixMulResult_helper<T<n, m>, T<m, k>> { using type = T<n, k>; };
template<class L, class R>
using MatrixMulResult = typename MatrixMulResult_helper<L, R>::type;
template<class L, class R>
MatrixMulResult<std::decay_t<L>, std::decay_t<R>>
operator*(L&& lhs, R&& rhs)
{
// ...
}
The compiler is now free to deduce L and R as references. MatrixMulResult<> makes sure that this function is only defined when (the decayed types of) L and R are respectively of the form T<n,m> and T<m,k>. It returns a T<n,k>.

Perfect forwarding of functions to build a function list class

Consider the following code that build a class storing functions.
// Function list class
template <class... F>
struct function_list
{
template <class... G>
constexpr function_list(G&&... g) noexcept
: _f{std::forward<G>(g)...}
{
}
std::tuple</* F... OR F&&... */> _f;
};
// Function list maker
template <class... F, class R = /* Can we compute the return type here? */>
constexpr R make_function_list(F&&... f)
{
return function_list<
/* decltype(std::forward<F>(f))...
* OR F...
* OR F&&...
*/>(std::forward<F>(f)...);
}
I would like these functions to be perfectly forwarded (regardless of whether they are function pointers, functors, lambdas...). But I don't exactly understand all the type deduction happening behind std::forward and universal references. In the code above, I have three questions:
Should _f be of type std::tuple<F...> or std::tuple<F&&...> (and why?)
Is it possible to deduce the return type R in the template parameter list (because doing it manually instead of auto/decltype(auto) would be helpful to understand what is going on)
In the maker, what the function_list template argument should be: decltype(std::forward<F>(f)...), F, or F&&... (and why?)
Note: the constructor of function_list is not meant to be called directly, instead make_function_list is doing the job.
EDIT:
Is this case safe, when the operator() of function_list (not shown here) is not guaranted to be called on the same statement?
template <class... F>
constexpr function_list<F...> make_function_list(F&&... f)
{
return function_list<F&&...>(std::forward<F>(f)...);
}
But I don't exactly understand all the type deduction happening behind std::forward and universal references.
It's quite simple to understand via an example.
template <typename T>
void f(T&&)
{
std::tuple<T>{}; // (0)
std::tuple<T&&>{}; // (1)
}
In the case of (0):
T is deduced as T for rvalues
T is deduced as T& for lvalues.
In the case of (1):
T is deduced as T&& for rvalues
T is deduced as T& for lvalues.
As you can see, the only difference between two is how rvalues are deduced.
Regarding std::forward, this is what it does:
template <typename T>
void g(T&&);
template <typename T>
void f(T&& x)
{
g(x) // (0)
g(std::forward<T>(x)); // (1)
}
In the case of (0):
x is always an lvalue.
In the case of (1):
x is casted to T&& if T is deduced as T.
x stays an lvalue otherwise.
std::forward basically retains the type category of x by looking at how T was deduced.
Should _f be of type std::tuple<F...> or std::tuple<F&&...>
I think that in your case it should be std::tuple<F...>, as you want to store either lvalue references or values.
std::tuple<F&&...> would store either lvalue references or rvalue references - that would lead to dangling references in the case of temporaries.
Is it possible to deduce the return type R in the template parameter list
Yes, it is just function_list<F...>.
template <class... F, class R = function_list<F...>>
constexpr R make_function_list(F&&... f)
{
return function_list<F...>(std::forward<F>(f)...);
}
You don't even need the R template parameter.
template <class... F>
constexpr function_list<F...> make_function_list(F&&... f)
{
return function_list<F...>(std::forward<F>(f)...);
}
In the maker, what the function_list template argument should be: decltype(std::forward<F>(f)...), F, or F&&...
function_list should take F... as a template parameter for the reasons listed at the beginning of this answer (i.e. avoiding dangling references to temporaries).
It should still take std::forward<F>(f)... as its arguments to allow rvalues to be forwarded as such (i.e. moving rvalues into function_list's tuple).
If they are F&&, then if you pass a temporary to make_function_list, the returned class containing a tuple will store an rvalue reference to the temporary passed to make_function_list.
On the next line, it is now a dangling reference.
This seems bad in most use cases. This is not actually bad in all use cases; forward_as_tuple does this. But such use cases are not general use cases. The pattern is extremely brittle and dangerous.
In general, if you are returning a T&&, you want to return it as a T. This can cause a copy of the object; but the alternative is danging-reference-hell.
This gives us:
template<class... Fs>
struct function_list {
template<class... Gs>
explicit constexpr function_list(Gs&&... gs) noexcept
: fs(std::forward<Gs>(gs)...)
{}
std::tuple<Fs...> fs;
};
template<class... Fs, class R = function_list<Fs...>>
constexpr R make_function_list(Fs&&... fs) {
return R(std::forward<Fs>(fs)...);
}
Also make function_list's ctor explicit, because in the 1 argument case it devolves to a rather greedy implicit conversion constructor. This can be fixed but takes more effort than it is worth.
operator() requires an instance. A type name is not an instance.
It depends on what function_list is for. There basically are two cases:
function_list is a temporary helper that should never outlive the statement it appears in. Here we can store references to functions and perfect-forward each of them to the point of invocation:
template <class... F>
struct function_list
{
std::tuple<F&&...> f_;
// no need to make this template
constexpr function_list(F&&... f) noexcept
: f_{std::forward<F>(f)...}
{}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a)
{
return std::invoke(std::get<i>(f_), std::forward<A>(a)...);
}
};
function_list is a wrapper/container object akin to std::bind, in this case you'd want to store decayed copies of the functions to avoid dangling references and perfect-forwarding in this context would mean forwarding functions to the constructors of their decayed versions in f_ and then at the point of call imbuing the decayed functions with value category of the function_list itself:
template <class... F>
struct function_list
{
std::tuple<std::decay_t<F>...> f_;
template <typename... G>
constexpr function_list(G&&... g)
: f_{std::forward<G>(g)...}
{}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) &
{
return std::invoke(std::get<i>(f_), std::forward<A>(a)...);
}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) const&
{
return std::invoke(std::get<i>(f_), std::forward<A>(a)...);
}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) &&
{
return std::invoke(std::get<i>(std::move(f_)), std::forward<A>(a)...);
}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) const&&
{
return std::invoke(std::get<i>(std::move(f_)), std::forward<A>(a)...);
}
};
As with std::bind if you actually want to store a reference, you must do so explicitly with std::reference_wrapper.
Construction is the same in both cases:
template <class... F>
constexpr auto make_function_list(F&&... f)
{
return function_list<F...>(std::forward<F>(f)...);
}

Perfect forwarding with a temporary function wrapper

Consider the following code in C++14, following the posts here, here and here:
// Include
#include <tuple>
#include <iostream>
#include <type_traits>
// Temporary function queue declaration
template <class... F>
class temporary_function_queue;
// Apply function queue declaration
template <class... F>
constexpr temporary_function_queue<F&&...> apply_function_queue(F&&... f);
// Temporary function queue definition
template <class... F>
class temporary_function_queue final
{
// Types
private:
using const_lvalue_reference = const temporary_function_queue&;
using rvalue_reference = temporary_function_queue&&;
using temporary_type = temporary_function_queue<F&&...>;
using data_type = std::tuple<F&&...>;
// Lifecycle
private:
temporary_function_queue(rvalue_reference) = default;
temporary_function_queue(const_lvalue_reference) = delete;
temporary_function_queue operator=(rvalue_reference) = delete;
temporary_function_queue operator=(const_lvalue_reference) = delete;
explicit constexpr temporary_function_queue(F&&... f)
: _f{std::forward<F>(f)...}
{
}
// Temporary creator
public:
friend constexpr temporary_type apply_function_queue<>(F&&... f);
// Apply function queue declaration
public:
template <class... Args>
decltype(auto) operator()(Args&&... args) const&&
{
// Do I need to do std::forward on f0 too? If so, how?
return std::get<0>(_f)(std::forward<Args>(args)...);
}
// Data members
private:
data_type _f;
};
// Apply function queue definition
template <class... F>
constexpr temporary_function_queue<F&&...> apply_function_queue(F&&... f)
{
return temporary_function_queue<F&&...>(std::forward<F>(f)...);
}
/* Example of use
int main(int argc, char* argv[])
{
apply_function_queue(
[](auto i){std::cout<<0<<std::endl;},
[](auto i){std::cout<<1<<std::endl;}
)(0);
return 0;
}
*/
The goal is to produce the following call syntax:
apply_function_queue(f0, f1, f2)(a, b, c, d, e);
Where f0, f1, f2 are either function pointers, functors, lambdas..., and where a, b, c, d, e are arguments to be perfectly forwarded. This function should produce a temporary type, and then call the operator() of that temporary type, and this operator should do a perfect forwarding of fn (for now f0, it will be changed later) with the arguments a, b, c, d, e.... The temporary_function_queue should not be usable in any other context.
The problem is that I am a little lost with the forwarding, universal references and lvalue references... Is the code shown above safe? If not what example of use would lead to undefined behaviour? And in that case, how to make it safe, and efficient (ideally, I would like no runtime overhead on most compilers with -O3)?
Note: Dangling references are quite likely to happen with this approach.
auto make_f(); // return by value
auto&& q = apply_function_queue(make_f());
// q holds dangling rvalue reference
q(a, b, c); // whoops...
First some remarks about wording and deduction. Let:
template<class... T> void f(T&&... p) {}
Note: When this template is instantiated, there are two different packs here: T... and T&&....
Call it with an lvalue or type R and an rvalue of type Q:
R a;
f(a, Q{});
Now T... will be R&, Q but T&&... will be R&, Q&&.
Forwarding the pack p will result in the T&&... pack.
'decltype'(std::forward<T>(p)...) === T&&...
(Note: You can't actually apply decltype here - it's just for illustration.)
Therefore, I'll call the pack that is actually deduced (T...) the deduced types/pack and the result of adding rvalue references / forwarding (T&&...) the forwarded types/pack.
Applying && everywhere inside the class as well as in the return type of apply_function_queue is superfluous. (If you return temporary_function_queue<F&&...> from apply_function_queue there is no need for && inside temporary_function_queue. And if you apply && inside the class everywhere there is no need to return temporary_function_queue<F&&...>.)
You either instantiate the class template with the deduced pack and add && everywhere you want references our you instantiate the class template with the forwarding pack and don't add &&.
It is required to have the deduced types available in the class. (Because the friend declaration uses both F... and F&&....) So you'll want to remove && from the return type of apply_function_queue.
You'll need to change some declarations:
apply_function_queue
forward declaration:
template <class... F>
constexpr temporary_function_queue<F...> apply_function_queue(F&&... f);
definition:
template <class... F>
constexpr temporary_function_queue<F...> apply_function_queue(F&&... f)
{
return temporary_function_queue<F...>(std::forward<F>(f)...);
}
temporary_type
The class instance type is temporary_function_queue<F...> not temporary_function_queue<F&&...>!
using temporary_type = temporary_function_queue<F...>;
friend declaration
friend constexpr temporary_type apply_function_queue<F...>(F&&... f);
If you want perfect forwarding of rvalue function types in the function call operator you'll have to resort to manual casting / forwarding I think.
Inside decltype(auto) operator()(Args&&... args) const&& you'll find that
decltype(std::get<0>(_f)) === std::tuple_element_t<0u, data_type>&
which by reference collapsing rules is a lvalue reference. What you actually want, in order to forward the elements from the tuple is the tuple_element::type.
Thus you'd have to directly cast to the actual type in the tuple:
return static_cast<std::tuple_element_t<0u, data_type>>(
std::get<0>(_f))(std::forward<Args>(args)...);
or forward (which will have the same effect through reference collapsing):
return std::forward<std::tuple_element_t<0u, data_type>>(
std::get<0>(_f))(std::forward<Args>(args)...);