Is it possible to conditionally choose the capture method of a lambda based on compile time information? For example...
auto monad = [](auto && captive) {
return [(?)captive = std::forward<decltype(captive)>(captive)](auto && a) {
return 1;
};
};
I want capture by reference if decltype(captive)
is a std::reference_wrapper
, and everything else captured by value.
The [=] you're referring to is part of the capture list for the lambda expression. This tells C++ that the code inside the lambda expression is initialized so that the lambda gets a copy of all the local variables it uses when it's created.
A capture clause of lambda definition is used to specify which variables are captured and whether they are captured by reference or by value. An empty capture closure [ ], indicates that no variables are used by lambda which means it can only access variables that are local to it.
The mutable keyword is used so that the body of the lambda expression can modify its copies of the external variables x and y , which the lambda expression captures by value. Because the lambda expression captures the original variables x and y by value, their values remain 1 after the lambda executes. C++ Copy.
Lambda capture type cannot be controlled by template-dependent names.
However, you could achieve the desired effect by delegating creating the inner lambda to an overloaded function:
template<class T>
auto make_monad(T&& arg) {
return [captive = std::forward<T>(arg)](auto&& a) {
std::cout << __PRETTY_FUNCTION__ << " " << a << '\n';
return 1;
};
}
template<class T>
auto make_monad(std::reference_wrapper<T> arg) {
return [&captive = static_cast<T&>(arg)](auto&& a) {
std::cout << __PRETTY_FUNCTION__ << " " << a << '\n';
return 1;
};
}
int main() {
auto monad = [](auto&& captive) {
return make_monad(std::forward<decltype(captive)>(captive));
};
int n = 1;
monad(1)(1);
monad(n)(2);
monad(std::ref(n))(3);
}
Outputs:
make_monad(T&&)::<lambda(auto:1&&)> [with auto:1 = int; T = int] 1
make_monad(T&&)::<lambda(auto:1&&)> [with auto:1 = int; T = int&] 2
make_monad(std::reference_wrapper<_Tp>)::<lambda(auto:2&&)> [with auto:2 = int; T = int] 3
I don't want to capture reference_wrapper by reference, I want to capture the reference it holds by reference. Reference wrapper does it's best to be a like a reference, but since the call operator (aka, "." operator) cannot be overloaded, it fails pretty miserably at the end of the day.
In this case you do not need to change the capture type for std::reference_wrapper<T>
. Instead, you may like to capture it by value like any other type of argument and at the usage site unwrap the argument first:
template<class T> T& unwrap(T& t) { return t; }
template<class T> T& unwrap(std::reference_wrapper<T> t) { return t; }
auto monad = [](auto && captive) {
return [captive](auto && a) { // <--- Capture by value.
auto& captive_ref = unwrap(captive); // <--- Unwrap before usage.
return 1;
};
};
It doesn't answer your question but your comment, How to use operator .
:
You may add those two overloads:
template <typename T>
T& get_reference_object(T&& t) { return t; }
template <typename T>
T& get_reference_object(std::reference_wrapper<T> t) { return t.get(); }
and then you may use get_reference_object(arg).foo
inside your lambda:
auto monad = [](auto && captive) {
return [captive = captive](auto&& a) { return get_reference_object(captive).foo(a); };
};
Live example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With