我正在使用一些“元方法”来执行编译时调用签名检测。虽然这个解决方案对我有用,但这只是因为我选择了相当具体的签名。这个解决方案不够通用,如下面的代码所示,由于
std::is_invocable...
的允许,隐式转换很容易导致误报。
#include <type_traits>
#include <utility>
template <class Foo, class... Args>
static constexpr auto canCallWith() {
return std::is_invocable_v<Foo, Args...>;
}
template <class Foo, class Ret, class... Args>
static constexpr auto canRetWith() {
return std::is_same<Ret, typename std::invoke_result<Foo, Args...>::type>::value;
}
template <class Foo, class Ret, class... Args>
static constexpr auto sigmatch() {
if constexpr (canCallWith<Foo, Args...>()) {
if constexpr (canRetWith<Foo, Ret, Args...>()) { return true; }
}
return false;
}
#include <iostream>
void foo(int, double = 56) {}
int main() {
std::cout << sigmatch<decltype(foo), void, int, double>() << '\n'; // TRUE
std::cout << sigmatch<decltype(foo), void, int>() << '\n'; // FALSE - def param value doesn't trip
std::cout << sigmatch<decltype(foo), void, double, int>() << '\n'; // TRUE - a "false positive" / implicit conversion
return 0;
}
我想知道是否有办法改变实现来禁止这种行为并仅执行严格的签名匹配?
PS。我的代码库仅限于 C++17,因此有这个标签,尽管我确信其他用户也可能会欣赏更现代的解决方案。
std::invocalbe
必须考虑转换才有效。如果您想检查确切的类型,可以使用 std::function
的 CTAD 来获取可调用对象的返回类型和参数类型,然后检查确切的类型:
#include <type_traits>
#include <utility>
#include <functional>
template <typename T> struct funtype {
using type = decltype( std::function{std::declval<T>()});
};
template <typename T, class Ret, class...Args>
static constexpr auto sigmatch() {
return std::is_same_v<typename funtype<T>::type, std::function<Ret(Args...)>>;
}
void foo(int, double = 56) {}
int main() {
std::cout << sigmatch<decltype(foo), void, int, double>() << '\n'; // TRUE
std::cout << sigmatch<decltype(foo), void, int>() << '\n'; // FALSE - def param value doesn't trip
std::cout << sigmatch<decltype(foo), void, double, int>() << '\n'; // TRUE - a "false positive" / implicit conversion
return 0;
}
输出:
1
0
0