通用 lambda 中 auto& 的完美转发
Perfect forwaring of auto&& in generic lambda
下一个通用 lambda 用于保护在线程之间共享资源的任何操作:
auto mutexed = [mtx(std::mutex{})](auto &&fn, auto &&...args) mutable {
std::unique_lock lo(mtx);
return fn(args...);
//return std::forward<decltype(fn)>(fn)(std::forward<decltype(args)>(args)...);
};
decltype(fn)
完美转发无效。它适用于值和右值引用。但不适用于左值引用。
写 decltype((fn))
将适用于左值引用,但不适用于右值。
所以问题。
如何使引用和右值引用都能完美转发?
使线程不破坏标准输出的示例:https://gcc.godbolt.org/z/KsWM6Pq6x
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::chrono_literals;
using std::cout, std::clog, std::cerr, std::endl;
int main() {
cout << "Hello, World!" << endl;
auto mutexed = [mtx(std::mutex{})](auto &&fn, auto &&...args) mutable {
std::unique_lock lo(mtx);
//return fn(args...);
return std::forward<decltype(fn)>(fn)(std::forward<decltype(args)>(args)...);
};
int counter = 0;
auto t1 = std::thread( [&](){
while(1){
std::this_thread::sleep_for(10ms);
// mutexed( [](int&&cnt){ cout << cnt++ << endl; }, std::move(counter) ); //FAILS HERE
mutexed( [](int&cnt){ cout << cnt++ << endl; }, counter ); //lval ref works fine
}
});
auto t2 = std::thread([&]{
while(1){
std::this_thread::sleep_for(10ms);
mutexed( []{ cout << "done long_operation_2" << endl;});}
});
t1.join();
t2.join();
}
C++11、14、17 和 C++20、23 中的工作解决方案
int main() {
auto lambda20 = []<class F, class...Ts>(F &&fn, Ts &&...args) {
return std::forward<F>(fn)(std::forward<Ts>(args)...);
};
auto lambda14 = [](auto &&fn, auto &&...args) {
return std::forward<
std::conditional_t<
std::is_rvalue_reference_v<decltype(fn)>,
typename std::remove_reference_t<decltype(fn)>,
decltype(fn)>
>(fn)(
std::forward<
std::conditional_t<std::is_rvalue_reference<decltype(args)>::value,
typename std::remove_reference<decltype(args)>::type,
decltype(args)
>>(args)...);
};
int inter = 20;
lambda20([](int x) { cout << "asdf20 x" << endl; }, inter);
lambda20([](int &x) { cout << "asdf20 &x" << endl; }, inter);
lambda20([](int &&x) { cout << "asdf20 &&x" << endl; }, std::move(inter));
lambda14([](int x) { cout << "asdf14 x" << endl; }, inter);
lambda14([](int &x) { cout << "asdf14 &x" << endl; }, inter);
lambda14([](int &&x) { cout << "asdf14 &&x" << endl; }, std::move(inter));
return 0;
}
Scott Meyers 8 岁的 C++ pre-20 解决方案 https://scottmeyers.blogspot.com/2013/05/c14-lambdas-and-perfect-forwarding.html
在 C++20 及更高版本中
auto lambda20 = []<class F, class...Ts>(F &&fn, Ts &&...args) {
return std::forward<F>(fn)(std::forward<Ts>(args)...);
};
在 C++pre20 中:C++11,14,17
auto lambda14 = [](auto &&fn, auto &&...args) {
return std::forward<
std::conditional_t<
std::is_rvalue_reference_v<decltype(fn)>,
typename std::remove_reference_t<decltype(fn)>,
decltype(fn)>
>(fn)(
std::forward<
std::conditional_t<std::is_rvalue_reference<decltype(args)>::value,
typename std::remove_reference<decltype(args)>::type,
decltype(args)
>>(args)...);
};
例子
#include <iostream>
using namespace std;
int main() {
auto lambda20 = []<class F, class...Ts>(F &&fn, Ts &&...args) {
return std::forward<F>(fn)(std::forward<Ts>(args)...);
};
auto lambda14 = [](auto &&fn, auto &&...args) {
return std::forward<
std::conditional_t<
std::is_rvalue_reference_v<decltype(fn)>,
typename std::remove_reference_t<decltype(fn)>,
decltype(fn)>
>(fn)(
std::forward<
std::conditional_t<std::is_rvalue_reference<decltype(args)>::value,
typename std::remove_reference<decltype(args)>::type,
decltype(args)
>>(args)...);
};
int inter = 20;
lambda20([](int x) { cout << "asdf20 x" << endl; }, inter);
lambda20([](int &x) { cout << "asdf20 &x" << endl; }, inter);
lambda20([](int &&x) { cout << "asdf20 &&x" << endl; }, std::move(inter));
lambda14([](int x) { cout << "asdf14 x" << endl; }, inter);
lambda14([](int &x) { cout << "asdf14 &x" << endl; }, inter);
lambda14([](int &&x) { cout << "asdf14 &&x" << endl; }, std::move(inter));
return 0;
}
Scott Meyers 8 岁的 C++ pre-20 解决方案https://scottmeyers.blogspot.com/2013/05/c14-lambdas-and-perfect-forwarding.html
下一个通用 lambda 用于保护在线程之间共享资源的任何操作:
auto mutexed = [mtx(std::mutex{})](auto &&fn, auto &&...args) mutable {
std::unique_lock lo(mtx);
return fn(args...);
//return std::forward<decltype(fn)>(fn)(std::forward<decltype(args)>(args)...);
};
decltype(fn)
完美转发无效。它适用于值和右值引用。但不适用于左值引用。
写 decltype((fn))
将适用于左值引用,但不适用于右值。
所以问题。
如何使引用和右值引用都能完美转发?
使线程不破坏标准输出的示例:https://gcc.godbolt.org/z/KsWM6Pq6x
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std::chrono_literals;
using std::cout, std::clog, std::cerr, std::endl;
int main() {
cout << "Hello, World!" << endl;
auto mutexed = [mtx(std::mutex{})](auto &&fn, auto &&...args) mutable {
std::unique_lock lo(mtx);
//return fn(args...);
return std::forward<decltype(fn)>(fn)(std::forward<decltype(args)>(args)...);
};
int counter = 0;
auto t1 = std::thread( [&](){
while(1){
std::this_thread::sleep_for(10ms);
// mutexed( [](int&&cnt){ cout << cnt++ << endl; }, std::move(counter) ); //FAILS HERE
mutexed( [](int&cnt){ cout << cnt++ << endl; }, counter ); //lval ref works fine
}
});
auto t2 = std::thread([&]{
while(1){
std::this_thread::sleep_for(10ms);
mutexed( []{ cout << "done long_operation_2" << endl;});}
});
t1.join();
t2.join();
}
C++11、14、17 和 C++20、23 中的工作解决方案
int main() {
auto lambda20 = []<class F, class...Ts>(F &&fn, Ts &&...args) {
return std::forward<F>(fn)(std::forward<Ts>(args)...);
};
auto lambda14 = [](auto &&fn, auto &&...args) {
return std::forward<
std::conditional_t<
std::is_rvalue_reference_v<decltype(fn)>,
typename std::remove_reference_t<decltype(fn)>,
decltype(fn)>
>(fn)(
std::forward<
std::conditional_t<std::is_rvalue_reference<decltype(args)>::value,
typename std::remove_reference<decltype(args)>::type,
decltype(args)
>>(args)...);
};
int inter = 20;
lambda20([](int x) { cout << "asdf20 x" << endl; }, inter);
lambda20([](int &x) { cout << "asdf20 &x" << endl; }, inter);
lambda20([](int &&x) { cout << "asdf20 &&x" << endl; }, std::move(inter));
lambda14([](int x) { cout << "asdf14 x" << endl; }, inter);
lambda14([](int &x) { cout << "asdf14 &x" << endl; }, inter);
lambda14([](int &&x) { cout << "asdf14 &&x" << endl; }, std::move(inter));
return 0;
}
Scott Meyers 8 岁的 C++ pre-20 解决方案 https://scottmeyers.blogspot.com/2013/05/c14-lambdas-and-perfect-forwarding.html
在 C++20 及更高版本中
auto lambda20 = []<class F, class...Ts>(F &&fn, Ts &&...args) {
return std::forward<F>(fn)(std::forward<Ts>(args)...);
};
在 C++pre20 中:C++11,14,17
auto lambda14 = [](auto &&fn, auto &&...args) {
return std::forward<
std::conditional_t<
std::is_rvalue_reference_v<decltype(fn)>,
typename std::remove_reference_t<decltype(fn)>,
decltype(fn)>
>(fn)(
std::forward<
std::conditional_t<std::is_rvalue_reference<decltype(args)>::value,
typename std::remove_reference<decltype(args)>::type,
decltype(args)
>>(args)...);
};
例子
#include <iostream>
using namespace std;
int main() {
auto lambda20 = []<class F, class...Ts>(F &&fn, Ts &&...args) {
return std::forward<F>(fn)(std::forward<Ts>(args)...);
};
auto lambda14 = [](auto &&fn, auto &&...args) {
return std::forward<
std::conditional_t<
std::is_rvalue_reference_v<decltype(fn)>,
typename std::remove_reference_t<decltype(fn)>,
decltype(fn)>
>(fn)(
std::forward<
std::conditional_t<std::is_rvalue_reference<decltype(args)>::value,
typename std::remove_reference<decltype(args)>::type,
decltype(args)
>>(args)...);
};
int inter = 20;
lambda20([](int x) { cout << "asdf20 x" << endl; }, inter);
lambda20([](int &x) { cout << "asdf20 &x" << endl; }, inter);
lambda20([](int &&x) { cout << "asdf20 &&x" << endl; }, std::move(inter));
lambda14([](int x) { cout << "asdf14 x" << endl; }, inter);
lambda14([](int &x) { cout << "asdf14 &x" << endl; }, inter);
lambda14([](int &&x) { cout << "asdf14 &&x" << endl; }, std::move(inter));
return 0;
}
Scott Meyers 8 岁的 C++ pre-20 解决方案https://scottmeyers.blogspot.com/2013/05/c14-lambdas-and-perfect-forwarding.html