gpt4 book ai didi

c++ - 如何使用模板函数的函数签名进行 SFINAE

转载 作者:行者123 更新时间:2023-12-02 09:48:23 28 4
gpt4 key购买 nike

我有一个代码,它接受一个函数并根据函数签名执行它,如下所示:

template <int Num>
struct Value {
int value[Num];
};

struct Executor {
template <int N>
void do_exec(std::vector<Value<N>>& n, void (&func) (Value<N>&)) {
for (auto& item : n)
func(item);
}

template <int N>
void do_exec(std::vector<Value<N>>& n, void (&func) (Value<N>&, int)) {
for (int i = 0; i != n.size(); i++)
func(n[i], i);
}
};
当用户传入以下函数之一时, Executor运行 do_exec()匹配它的签名。
template <int N>
void f1(Value<N>& item)
{
for (auto& i : item.value) {
i = 123;
}
}

template <int N>
void f2(Value<N>& item, int d)
{
for (auto& i : item.value) {
i = d;
}
}

int main()
{
Executor exec;
std::vector<Value<3>> vec(10);
exec.do_exec(vec, f1);
}
我想扩展这段代码,所以它可以使用 lambda 函数,因为在实际代码中,几乎所有代理都会使用 GENERIC lambdas 调用它。
我尝试用 std::function 替换仿函数,但它失败了,因为 lambda 不是 std::function并且类型推断并没有真正发生。
然后我尝试将两个模板参数和 SFINAE 取出一个与签名不匹配的参数,如下所示:
template <typename Fn, typename T, typename = void>
struct HasIndex : std::false_type {};

template <typename Fn, typename T>
struct HasIndex<Fn, T, std::void_t<std::invoke_result_t<Fn, T&, int>>> : std::true_type {};

struct Executor {
template <int N, typename Fn, std::enable_if_t<!HasIndex<Fn, Value<N>>::value, int> = 1>
void do_exec(std::vector<Value<N>>& n, Fn func) {
for (auto& item : n)
func(item);
}

template <int N, typename Fn, std::enable_if_t<HasIndex<Fn, Value<N>>::value, int> = 1>
void do_exec(std::vector<Value<N>>& n, Fn func) {
for (int i = 0; i != n.size(); i++)
func(n[i], i);
}
};
这也不起作用,因为执行程序将采用的函数始终是模板函数(通用 Lambda)。我不知道如何解决这个问题,任何帮助表示赞赏。
请使用 c++14 解决方案(我知道 invoke_result 是 c++17)
https://godbolt.org/z/W7z3Mv

最佳答案

修复相当简单。首先,我会使用 std::is_invocable_v 从类型特征库中测试 SFINAE 机制中的兼容函数签名。换行符使模板签名保持可读性,我发现:

template<
int N,
typename Fn,
std::enable_if_t<std::is_invocable_v<Fn, Value<N>&>>* = nullptr
>
void do_exec(std::vector<Value<N>>& n, Fn func) {
[...]
}

template<
int N,
typename Fn,
std::enable_if_t<std::is_invocable_v<Fn, Value<N>&, int>>* = nullptr
>
void do_exec(std::vector<Value<N>>& n, Fn func) {
[...]
}
这允许对函数和通用 lambda 的非模板引用,但以下内容尚无法使用:
template <int N>
void f1(Value<N>& item){ [...] }

int main(){
Executor exec;
std::vector<Value<3>> vec(10);
exec.do_exec(vec, f1);
}
对我来说,这失败了一个非常通用的模板参数推导/替换失败。要完成这项工作,您需要专门化 f1值为 N ,如:
int main(){
Executor exec;
std::vector<Value<3>> vec(10);
exec.do_exec(vec, f1<3>); // Fn is deduced as void(&)(Value<3>&) (I think)
}
Live Demo

C++14 兼容性更新
由于 std::is_invocable_v仅在 C++17 之后可用,您可以使用如下解决方法(未经过彻底测试,但我感觉很好):
template<typename F, typename ArgsTuple, typename Enable = void>
struct my_is_invocable_impl : std::false_type {};

template<typename F, typename... Args>
struct my_is_invocable_impl<
F,
std::tuple<Args...>,
decltype(std::declval<F>()(std::declval<Args>()...))
> : std::true_type {};

template<typename T, typename... Args>
constexpr bool my_is_invocable = my_is_invocable_impl<T, std::tuple<Args...>>::value;

// Some test cases
static_assert(my_is_invocable<void(*)(int, double), int, double>, "Oops");
static_assert(my_is_invocable<void(*)(void*), void*>, "Oops");
static_assert(my_is_invocable<void(*)()>, "Oops");
static_assert(!my_is_invocable<void(*)(int, double)>, "Oops");
static_assert(!my_is_invocable<void(*)(void*)>, "Oops");
这可以用作 std::is_invocable_v 的直接替代品。在上述解决方案中。有关完整示例,请参阅演示,包括通用 lambda。
Live Demo for C++14

关于c++ - 如何使用模板函数的函数签名进行 SFINAE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62884204/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com