Tôi đang cố gắng cung cấp một trình bao bọc xung quanh std::invoke
để thực hiện công việc suy ra loại chức năng ngay cả khi chức năng bị quá tải.
(Tôi đã hỏi một câu hỏi liên quan ngày hôm qua cho phiên bản con trỏ phương thức và phương thức).
Khi hàm có một đối số, mã này (C ++ 17) hoạt động như mong đợi trong điều kiện quá tải thông thường:
#include <functional>
template <typename ReturnType, typename ... Args>
using FunctionType = ReturnType (*)(Args...);
template <typename S, typename T>
auto Invoke (FunctionType<S, T> func, T arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, T&> func, T & arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, const T&> func, const T & arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, T&&> func, T && arg)
{
return std::invoke(func, std::move(arg));
}
Việc giảm sự phình mã rõ ràng là cần thiết cho nhiều đối số đầu vào, nhưng đó là một vấn đề riêng biệt.
Nếu người dùng có quá tải chỉ khác nhau bởi const / tham chiếu, như vậy:
#include <iostream>
void Foo (int &)
{
std::cout << "(int &)" << std::endl;
}
void Foo (const int &)
{
std::cout << "(const int &)" << std::endl;
}
void Foo (int &&)
{
std::cout << "(int &&)" << std::endl;
}
int main()
{
int num;
Foo(num);
Invoke(&Foo, num);
std::cout << std::endl;
Foo(0);
Invoke(&Foo, 0);
}
Sau đó Invoke
suy ra hàm không chính xác, với đầu ra g ++:
(int &)
(const int &)(int &&)
(const int &)
Và kêu vang ++:
(int &)
(const int &)(int &&)
(int &&)
(Cảm ơn geza vì đã chỉ ra rằng kết quả đầu ra của clang là khác nhau).
Vì vậy, Invoke
có hành vi không xác định.
Tôi nghi ngờ rằng siêu lập trình sẽ là cách để tiếp cận vấn đề này. Bất kể, có thể xử lý loại khấu trừ chính xác tại Invoke
trang web?
(int &&)
hai lần cho trường hợp thứ hai.
S
suy luận. Cố gắng nhận xét const T &
phiên bản Invoke
và thông báo lỗi. Ngoài ra nếu đối số được cung cấp rõ ràng ( Invoke<void>(&Foo, num)
) phiên bản chính xác được gọi.
Invoke
, nó có thể khởi tạo nó bằng cả const và non-const Foo
. Và nó không kiểm tra loại trả về ( S
) giống nhau cho cả hai, vì vậy nó nói rằng nó không thể suy ra S
. Vì vậy, nó bỏ qua mẫu này. Trong khi khởi tạo const chỉ Invoke
có thể được thực hiện với const Foo
, vì vậy nó có thể suy ra S
trong trường hợp này. Do đó trình biên dịch sử dụng mẫu này.