Quá trình phân giải quá tải chỉ xảy ra khi (a) bạn đang gọi tên của hàm / toán tử hoặc (b) đang truyền nó tới một con trỏ (đến hàm hoặc hàm thành viên) bằng chữ ký rõ ràng.
Không có gì xảy ra ở đây.
std::function
lấy bất kỳ đối tượng tương thích với chữ ký của nó. Nó không có một con trỏ hàm cụ thể. (lambda không phải là hàm std và hàm std không phải là lambda)
Bây giờ trong các biến thể chức năng homebrew của tôi, đối với chữ ký, R(Args...)
tôi cũng chấp nhậnR(*)(Args...)
đối số (khớp chính xác) cho chính xác lý do này. Nhưng nó có nghĩa là nó nâng chữ ký "khớp chính xác" trên chữ ký "tương thích".
Vấn đề cốt lõi là một bộ quá tải không phải là một đối tượng C ++. Bạn có thể đặt tên cho một tập hợp quá tải, nhưng bạn không thể vượt qua nó "nguyên bản".
Bây giờ, bạn có thể tạo một tập hợp giả quá tải của một hàm như thế này:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) )
điều này tạo ra một đối tượng C ++ duy nhất có thể thực hiện phân giải quá tải trên tên hàm.
Mở rộng các macro, chúng tôi nhận được:
[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }
đó là khó chịu để viết. Một phiên bản đơn giản hơn, ít hữu ích hơn một chút ở đây:
[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }
chúng ta có một lambda có bất kỳ số lượng đối số nào, sau đó hoàn hảo chuyển tiếp chúng tới baz
.
Sau đó:
class Bar {
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};
làm. Chúng tôi trì hoãn độ phân giải quá tải vào lambda mà chúng tôi lưu trữ fun
, thay vì vượt quafun
trực tiếp bộ quá tải (điều này không thể giải quyết).
Đã có ít nhất một đề xuất để xác định một hoạt động trong ngôn ngữ C ++ để chuyển đổi tên hàm thành một đối tượng tập quá tải. Cho đến khi một đề xuất tiêu chuẩn như vậy là trong tiêu chuẩn, OVERLOADS_OF
macro là hữu ích.
Bạn có thể tiến thêm một bước và hỗ trợ con trỏ cast-to-tương thích-hàm-con trỏ.
struct baz_overloads {
template<class...Ts>
auto operator()(Ts&&...ts)const
RETURNS( baz(std::forward<Ts>(ts)...) );
template<class R, class...Args>
using fptr = R(*)(Args...);
//TODO: SFINAE-friendly support
template<class R, class...Ts>
operator fptr<R,Ts...>() const {
return [](Ts...ts)->R { return baz(std::forward<Ts>(ts)...); };
}
};
nhưng điều đó đang bắt đầu trở nên khó hiểu
Ví dụ sống .
#define OVERLOADS_T(...) \
struct { \
template<class...Ts> \
auto operator()(Ts&&...ts)const \
RETURNS( __VA_ARGS__(std::forward<Ts>(ts)...) ); \
\
template<class R, class...Args> \
using fptr = R(*)(Args...); \
\
template<class R, class...Ts> \
operator fptr<R,Ts...>() const { \
return [](Ts...ts)->R { return __VA_ARGS__(std::forward<Ts>(ts)...); }; \
} \
}