I am trying to make a function that can stage arbitrary computation with lambda. This function takes in any function and parameter pack and stages the computation in a lambda function. A conflict occurs when Args
contains both non-copyable lvalue reference and prvalue. The prvalue parameter will become dangling reference if we capture by reference. If we capture by value, we will try to call copy constructor of the lvalue reference. Neither will compile.
Is there a correct way to do it in C 20? I've read many related posts. Many of them give the capture by value forwarding approach as below.
#include <iostream>
#include <memory>
using namespace std;
void g(std::unique_ptr<int>& a, std::unique_ptr<int> b) {
if (a) {
cout << *a << " ";
}
if (b) {
cout << *b;
}
}
template <typename F, typename ... Args>
auto f(F&& func, Args&& ... args) {
// by reference
auto lamb = [func, &args...]() {
g(std::forward<Args>(args)...);
};
// by value
/*auto lamb = [func, ... args = std::forward<Args>(args)]() mutable {
g(std::forward<Args>(args)...);
};*/
return lamb;
}
int main()
{
auto ptr = std::make_unique<int>(5);
auto ptr2 = std::make_unique<int>(6);
f(g, ptr, std::move(ptr2));
auto l = f(g, ptr, std::make_unique<int>(7));
l();
return 0;
}
CodePudding user response:
You can use a tuple
to store args...
and decide whether to store a value or a reference depending on whether the args...
are lvalue or rvalue. Then use std::apply
to forward parameters
template <typename F, typename ... Args>
auto f(F&& func, Args&& ... args) {
// by value or by reference
auto lamb = [func, args_tuple =
std::tuple<Args...>(std::forward<Args>(args)...)]() mutable {
std::apply([func](auto&... args) {
func(std::forward<Args>(args)...);
}, args_tuple);
};
return lamb;
}
CodePudding user response:
You could pass your lvalues using std::ref
.
This way g
would receive:
- a
std::unique_ptr<int>&
for the lvalues. - a
std::unique_ptr
for the rvalues.
#include <functional> // ref
#include <iostream>
#include <memory>
void g(std::unique_ptr<int>& a, std::unique_ptr<int> b) {
if (a) {
std::cout << *a << " ";
}
if (b) {
std::cout << *b << "\n";
}
}
template <typename F, typename ... Args>
auto f(F&& func, Args&& ... args) {
auto lamb = [func, ... args = std::forward<Args>(args)]() mutable {
func(std::forward<Args>(args)...);
};
return lamb;
}
int main() {
auto ptr = std::make_unique<int>(5);
auto ptr2 = std::make_unique<int>(6);
auto l = f(g, std::ref(ptr), std::move(ptr2));
l();
auto ll = f(g, std::ref(ptr), std::make_unique<int>(7));
ll();
}
// Outputs:
// 5 6
// 5 7