Created
December 4, 2012 21:29
-
-
Save jbandela/4208898 to your computer and use it in GitHub Desktop.
Capture by Move Lambda - C++11
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <utility> | |
#include <iostream> | |
#include <memory> | |
#include <type_traits> | |
#include <functional> | |
template<class T> | |
using moved_value = std::reference_wrapper<T>; | |
template<class T,class F> | |
struct move_lambda{ | |
private: | |
T val; | |
F f_; | |
public: | |
move_lambda(T&& v, F f):val(std::move(v)),f_(f){}; | |
move_lambda(move_lambda&& other) = default; | |
move_lambda& operator=(move_lambda&& other) = default; | |
template<class... Args> | |
auto operator()(Args&& ...args) -> decltype(this->f_(moved_value<T>(this->val),std::forward<Args>(args)...)) | |
{ | |
moved_value<T> mv(val); | |
return f_(mv,std::forward<Args>(args)...); | |
} | |
move_lambda() = delete; | |
move_lambda(const move_lambda&) = delete; | |
move_lambda& operator=(const move_lambda&) = delete; | |
}; | |
template<class T,class F> | |
move_lambda<T,F>create_move_lambda(T&& t, F f){ | |
return move_lambda<T,F>(std::move(t),f); | |
} | |
// Unfortunately, std::function does not seem to support move-only callables | |
// See § 20.8.11.2.1 point 7 where it requires F be CopyConstructible | |
// From draft at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf | |
// Here is our movable replacement for std::function | |
template< class ReturnType, class... ParamTypes> | |
struct movable_function_base{ | |
virtual ReturnType callFunc(ParamTypes&&... p) = 0; | |
}; | |
template<class F, class ReturnType, class... ParamTypes> | |
struct movable_function_imp:public movable_function_base<ReturnType,ParamTypes...>{ | |
F f_; | |
virtual ReturnType callFunc(ParamTypes&&... p){ | |
return f_(std::forward<ParamTypes>(p)...); | |
} | |
explicit movable_function_imp(F&& f):f_(std::move(f)){}; | |
movable_function_imp() = delete; | |
movable_function_imp(const movable_function_imp&) = delete; | |
movable_function_imp& operator=(const movable_function_imp&) = delete; | |
}; | |
template<class FuncType> | |
struct movable_function{}; | |
template<class ReturnType, class... ParamTypes> | |
struct movable_function<ReturnType(ParamTypes...)>{ | |
std::unique_ptr<movable_function_base<ReturnType,ParamTypes...>> ptr_; | |
template<class F> | |
explicit movable_function(F&& f):ptr_(new movable_function_imp<F,ReturnType,ParamTypes...>(std::move(f))){} | |
movable_function(movable_function&& other) = default; | |
movable_function& operator=(movable_function&& other) = default; | |
template<class... Args> | |
auto operator()(Args&& ...args) -> ReturnType | |
{ | |
return ptr_->callFunc(std::forward<Args>(args)...); | |
} | |
movable_function() = delete; | |
movable_function(const movable_function&) = delete; | |
movable_function& operator=(const movable_function&) = delete; | |
}; | |
// This is a movable only object | |
struct TestMove{ | |
TestMove():k(0){} | |
TestMove(TestMove&& other):k(other.k){ | |
other.k = -1; | |
} | |
TestMove& operator=(TestMove&& other){ | |
k = other.k; | |
other.k = -1; | |
return *this; | |
} | |
int k; | |
TestMove(const TestMove&) = delete; | |
TestMove& operator=(const TestMove&) = delete; | |
}; | |
movable_function<void()> CreateLambda() | |
{ | |
// Pretend our TestMove is a HugeObject that we do not want to copy | |
typedef TestMove HugeObject; | |
// Manipulate our "HugeObject" | |
HugeObject hugeObj; | |
hugeObj.k = 9; | |
auto f = create_move_lambda(std::move(hugeObj),[](moved_value<HugeObject> hugeObj){// manipulate huge object | |
std::cout << hugeObj.get().k << std::endl; | |
}); | |
movable_function<void()> toReturn(std::move(f)); | |
return toReturn; | |
} | |
int main(){ | |
// A movable only type, not copyable | |
TestMove m; | |
m.k = 5; | |
// A movable only type, not copyable | |
TestMove m2; | |
m2.k = 6; | |
// Create a lambda that takes 2 parameters and returns int | |
auto lambda = create_move_lambda(std::move(m),[](moved_value<TestMove> m,int i,int)->int{ | |
std::cout << m.get().k << " " << i << std::endl;return 7; | |
}); | |
// Create a lambda that takes 0 parameters and returns void | |
auto lambda2 = create_move_lambda(std::move(m2),[](moved_value<TestMove> m){ | |
std::cout << m.get().k << std::endl; | |
}); | |
std::cout << lambda(1,2) << std::endl; | |
lambda2(); | |
// Compiler error if you try to copy | |
//auto lambda4 = lambda; | |
// Able to move | |
auto lambda3 = std::move(lambda2); | |
lambda3(); | |
auto lambda4 = CreateLambda(); | |
lambda4(); | |
// A movable only type, not copyable | |
TestMove m3; | |
m3.k = 6; | |
// You can take a reference instead of a moved_value if you want | |
auto lambda5 = create_move_lambda(std::move(m3),[](TestMove& m){ | |
std::cout << m.k << std::endl; | |
}); | |
lambda5(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment