gpt4 book ai didi

c++ - 带有 static_assert 的漂亮 sfinae

转载 作者:可可西里 更新时间:2023-11-01 18:35:29 25 4
gpt4 key购买 nike

我正在尝试创建一个事件管理器来注册接收者。为此,我希望能够构造一个具有给定参数的 std::function。但是,我希望最终用户能够轻松理解该错误。我考虑过使用 SFINAE 和依赖于类型的 static_assert 来执行此操作,但我遇到了麻烦,因为这两个函数在有效输入上变得不明确。此外,我希望用户可以收到多个错误原因。由于有两个失败点(提供无效的仿函数和提供错误的事件类型),我希望总共有 3 个函数,第一个是正确输入的函数,然后是不正确的输入(而不是有 4 个函数用于每个状态的组合)。

这可以用 c++17 的 if constexpr 解决,但我的目标平台是 c++14,所以需要使用其他方法。

我目前的尝试(只检查一种错误状态):

template <typename Event, typename Func>
auto register(Func &&func)
-> decltype(func_t<Event>(std::forward<Func>(func)), void()) {}

template <typename Event, typename Func>
void register(Func &&) {
static_assert(meta::delay_v<Func>, "Function object cant be constructed by function");
}

meta::delay_v 等于 false 但取决于其参数,因此 static_assert 在函数被调用之前不会被触发。


一个更复杂的用例是

template <typename Event, typename Func>
auto register(Func &&func)
-> decltype(func_t<Event>(std::forward<Func>(func))
,meta::is_in_tuple<Event, Events_Tuple>
,void()) {}

因此,如果第一个测试失败(func_t 构造),那么我们将对此static_assert,如果第二个测试失败,我们将static_assert关于那个。因此,如果第一个测试失败,无论第二个测试如何,我们都会使一些静态断言失败。然后,如果第一个测试通过,我们将打印第二个测试失败的信息。不必重写测试将是一个非常好的奖励。

最佳答案

当条件满足时,它们实际上是模棱两可的,因为两者都是有效的。
只有第一个函数有一个可以禁用它的 sfinae 表达式,因此第二个函数始终是一个可行的解决方案(当条件满足时是一个不明确的解决方案)。

您可以这样做:

template <typename Event, typename Func>
auto register(int, Func &&func)
-> decltype(func_t<Event>(std::forward<Func>(func)), void()) {}

template <typename Event, typename Func>
void register(char, Func &&) {
static_assert(meta::delay_v<Func>, "Function object cant be constructed by function");
}

template <typename Event, typename Func>
void register(Func &&func) {
register<Event>(0, std::forward<Func>(func));
}

在这种情况下,0(即 int)将强制编译器选择第一个函数并进行尝试。如果有效,则没有歧义(第二个想要 char),否则 0 可以转换为 char 并用于调用第二个功能。

如果你有两个以上的条件,你可以这样做:

template<int N> struct tag: tag<N-1> {};
template<> struct tag<0> {};

template <typename Event, typename Func>
auto register(tag<2>, Func &&func)
-> decltype(func_t<Event>(std::forward<Func>(func)), void()) {}

template <typename Event, typename Func>
auto register(tag<1>, Func &&func)
-> decltype(func_alternative_t<Event>(std::forward<Func>(func)), void()) {}

template <typename Event, typename Func>
void register(tag<0>, Func &&) {
static_assert(meta::delay_v<Func>, "Function object cant be constructed by function");
}

template <typename Event, typename Func>
void register(Func &&func) {
register<Event>(tag<2>{}, std::forward<Func>(func));
}

解的数量越多,使用的with tag的数量也就越多。适用于 int/char 技巧的相同原则在这里起作用。


作为旁注,正如@StoryTeller 在评论中提到的,请注意 register 是保留关键字,您不应在生产代码中使用它。

关于c++ - 带有 static_assert 的漂亮 sfinae,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41670445/

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