简介
Substitution Failed Is Not An Error,之前笔记介绍过。这里介绍一个拓展的应用,foo
函数传入可调用对象和对应的参数,之后会判定是否可以传入对应的参数,并返回函数的执行结果,具体参考代码。这是 SFINAE 一个经典应用
#include <iostream>
#include <type_traits>
#include <utility>
#include <string>
// 获取 Fn 调用 Args 的结果,std::invoke_result 可以针对 std::function,但是不可以针对 lambda
// 这里是针对任何可调用对象
template<typename Fn, typename ...Args>
using ResultOf = decltype(std::declval<Fn>()(std::declval<Args>()...));
template<typename Fn, typename ...Args>
struct CallWithArgs {
template<typename T, typename = void>
static constexpr std::false_type Check(...) {
return std::false_type{};
}
template<typename T, typename = ResultOf<Fn, Args...>>
static constexpr std::true_type Check(std::nullptr_t) {
return std::true_type{};
}
using IsCallable = decltype(Check<Fn>(nullptr));
using RetType = std::enable_if_t<std::is_same_v<IsCallable, std::true_type>, ResultOf<Fn, Args...>>;
};
template<typename Fn, typename ...Args>
std::enable_if_t<CallWithArgs<Fn, Args...>::IsCallable::value, typename CallWithArgs<Fn, Args...>::RetType>
foo(Fn &&f, Args &&...args) {
return std::forward<Fn>(f)(std::forward<Args>(args)...);
}
auto F = [](int n) {
return std::string("hello world!") + " " + std::to_string(n);
};
int f(int n) { return 10 * n; }
struct Callable {
std::string operator()(int n) {
std::string tmp;
for (int i = 0; i < n; ++i) {
tmp += "test-";
}
return tmp;
}
};
int main() {
std::cout << foo(F, 10) << std::endl;
std::cout << foo(f, 10) << std::endl;
Callable c;
std::cout << foo(c, 3) << std::endl;
// foo(c, "test"); // 错误,无法匹配
return 0;
}
进一步拓展,返回值同样可以作为模板:
#include <type_traits>
template<typename Fn, typename ...Args>
using ResultOf = decltype(std::declval<Fn>()(std::declval<Args>()...));
template<typename Fn, typename ...Args>
struct CallWithArgs {
template<typename T, typename = void>
static constexpr std::false_type Check(...) {
return std::false_type{};
}
template<typename T, typename = ResultOf<Fn, Args...>>
static constexpr std::true_type Check(std::nullptr_t) {
return std::true_type{};
}
using IsCallable = decltype(Check<Fn>(nullptr));
using RetType = std::enable_if_t<std::is_same_v<IsCallable, std::true_type>, ResultOf<Fn, Args...>>;
};
template<typename T>
class Future;
template<typename T>
struct IsFuture : std::false_type {
using Inner = T;
};
template<typename T>
struct IsFuture<Future<T>> : std::true_type {
using Inner = T;
};
template<typename Fn, typename ...Args>
typename CallWithArgs<Fn, Args...>::RetType foo(Fn &fn, Args &&...args) {
using RetType = typename CallWithArgs<Fn, Args...>::RetType;
if constexpr (IsFuture<RetType>::value) { // future<T> 的返回值类型
} else {
}
return std::forward<Fn>(fn)(std::forward<Args>(args)...);
}
template<typename Fn, typename ...Args>
typename CallWithArgs<Fn, Args...>::RetType then(Fn &fn, Args &&...args) {
using RetType = typename CallWithArgs<Fn, Args...>::RetType;
if constexpr (IsFuture<RetType>::value) { // future<T> 的返回值类型
} else {
}
return std::forward<Fn>(fn)(std::forward<Args>(args)...);
}
参考文档
http://kaiyuan.me/2018/05/08/sfinae/
https://marvinsblog.net/post/2019-09-11-cpp-sfinae-intro/
https://zhuanlan.zhihu.com/p/21314708
https://izualzhy.cn/SFINAE-and-enable_if