template<class...Sigs>
strucct functions:std::function<Sigs>...{
using std::function<Sigs>::operator()...;
template<class T,
std::enable_if<!std::is_same<std::decay_t<T>,fundtions>{}>,int> =0
>
functions(T&&t):
std::function<Sigs>(t)...
{}
};
ở trên là bản phác thảo C++ 17 của đối tượng thô cam lưu trữ nhiều hơn operator()
.
Một cách hiệu quả hơn sẽ chỉ lưu trữ đối tượng một lần, nhưng lưu trữ cách gọi nó theo nhiều cách. Và tôi đã bỏ qua rất nhiều chi tiết.
Nó không thực sự là std::function
, nhưng là loại tương thích; chức năng std chỉ lưu trữ một cách để gọi đối tượng.
Dưới đây là "chế độ xem chức năng" có bất kỳ số chữ ký nào. Nó không sở hữu đối tượng được gọi.
template<class Sig>
struct pinvoke_t;
template<class R, class...Args>
struct pinvoke_t<R(Args...)> {
R(*pf)(void*, Args&&...) = 0;
R invoke(void* p, Args...args)const{
return pf(p, std::forward<Args>(args)...);
}
template<class F, std::enable_if_t<!std::is_same<pinvoke_t, std::decay_t<F>>{}, int> =0>
pinvoke_t(F& f):
pf(+[](void* pf, Args&&...args)->R{
return (*static_cast<F*>(pf))(std::forward<Args>(args)...);
})
{}
pinvoke_t(pinvoke_t const&)=default;
pinvoke_t& operator=(pinvoke_t const&)=default;
pinvoke_t()=default;
};
template<class...Sigs>
struct invoke_view:pinvoke_t<Sigs>...
{
void* pv = 0;
explicit operator bool()const{ return pv; }
using pinvoke_t<Sigs>::invoke...;
template<class F, std::enable_if_t<!std::is_same<invoke_view, std::decay_t<F>>{}, int> =0>
invoke_view(F&& f):
pinvoke_t<Sigs>(f)...
{}
invoke_view()=default;
invoke_view(invoke_view const&)=default;
invoke_view& operator=(invoke_view const&)=default;
template<class...Args>
decltype(auto) operator()(Args&&...args)const{
return invoke(pv, std::forward<Args>(args)...);
}
};
Live example.
Tôi sử dụng C++ 17 using ...
vì việc triển khai cây nhị phân trong C++ 14 rất xấu.
Đối với trường hợp sử dụng của bạn, nó sẽ looke như:
auto func_object = [](int i = 0){};
invoke_view<void(), void(int)> f1 = func_object;
std::function<void(int)> f3 = f1; // works
std::function<void()> f4 = f1; // works
lưu ý rằng việc thiếu quản lý đời trong invoke_view
có nghĩa là ở trên chỉ hoạt động khi func_object
tiếp tục tồn tại. (Nếu chúng ta tạo một khung nhìn gọi đến một khung nhìn gọi, khung nhìn gọi "bên trong" cũng được lưu trữ bởi con trỏ, vì vậy phải tiếp tục tồn tại; không phải trường hợp nếu chúng ta lưu trữ khung nhìn gọi trong một hàm std).
Quản lý lâu dài mục tiêu, được thực hiện đúng, mất một chút công việc. Bạn sẽ muốn sử dụng một tối ưu hóa bộ đệm nhỏ với một con trỏ thông minh tùy chọn hoặc một cái gì đó để có được hiệu suất hợp lý với lambdas nhỏ và tránh các chi phí của việc phân bổ đống.
Một giải pháp phân bổ heap đơn giản luôn luôn heap sẽ thay thế void*
bằng unique_ptr<void, void(*)(void*)>
và lưu trữ { new T(t), [](void* ptr){static_cast<T*>(ptr)->~T();} }
trong đó (hoặc tương tự).
Giải pháp đó làm cho đối tượng hàm chỉ di chuyển; làm cho nó có thể sao chép cũng yêu cầu loại bỏ một hoạt động sao chép.
Bạn dự định gọi f1 hoặc f2 như thế nào (Với giá trị mặc định)? – nakiya
@nakiya Nó dành cho việc triển khai tín hiệu và khe yêu cầu bản sao của khe (chức năng) khi thực hiện kết nối, tín hiệu sẽ gọi khe với thông số được truyền từ trang gọi của tín hiệu. Nó sẽ được tốt đẹp để cho phép khe với các thông số mặc định để kết nối với bất kỳ tín hiệu với một chữ ký có thể gọi nó. –