【问题标题】:User-defined deduction guide for lambda用户自定义的 lambda 推导指南
【发布时间】:2019-02-01 12:39:26
【问题描述】:

今天我正在为 C++ 模板而苦苦挣扎。这是我在 JNI 代码中方便异常处理的简单代码。

template<typename T>
std::optional<T> handleNativeCrash(JNIEnv *env, std::function<T(void)> f) {
    try {
        return std::optional<T>(f());
    }
    catch (const std::exception &e) {
        jniThrowException(env, e.what());
        return {};
    }
}

当我尝试在不指定 T 的情况下使用它时,Clang 大致失败并出现此错误

no matching function for call to 'handleNativeCrash'
      return my_namespace::handleNativeCrash(env, [&]{
             ^~~~~~~~~~~~~~~~~~~~~
  /......../jni.cpp:116:39)'
      std::optional<T> handleNativeCrash(JNIEnv *env, std::function<T(void)> f) {
                       ^
  1 error generated.

我想从 lambda 返回类型自动推断出T。我试图写演绎指南,但似乎我无法为全局函数编写它。我尝试创建仅包含此功能的简单模板结构,但我也失败了。看来我不是很懂 C++ 模板元编程。

我的第一次尝试是这样的,但是 Clang 刚刚崩溃,抱怨语法非法并打印了它的回溯。我会尽快报告错误,但我需要先完成这项工作。

template<typename T>
handleNativeCrash(JNIEnv *env, std::function<T(void)> f) -> handleNativeCrash<decltype(f())>;

我怎样才能实现我的目标?

【问题讨论】:

  • lambda 不是std::function。最好使用通用的Callable 类型。
  • 关于你的clang崩溃:clang是对的它是非法的(缺少auto),错误的崩溃。

标签: c++ c++17 template-argument-deduction


【解决方案1】:

你不能为此使用模板推导,它不够聪明,只能用于匹配。

但你可以手动推断:

template<class Callable>
auto handleNativeCrash(JNIEnv *env, Callable f)
-> std::optional<decltype(f())>
{
    try {
        return f();
    }
    catch (const std::exception &e) {
        jniThrowException(env, e.what());
        return {};
    }
}

Simplified live demo

【讨论】:

  • 为什么不使用自动退货类型扣除?你真的想使用 SFINAE 吗?
  • @dedup 这是一个选项,但基本上我不想在函数 impl 中隐藏返回类型(非可选)。
【解决方案2】:

问题是 lambda 可以转换为 std::function 但不是 std::function

所以T类型不能推导出因为你没有std::functionstd::function因为你不知道T所以不能得到。

一种先有鸡还是先有蛋的问题。

所以你必须将std::function 传递给handleNativeCrash()

return my_namespace::handleNativeCrash(env, std::function{[&]{/*...*/}});

或者您必须收到 YSC 答案中的通用可调用对象,或者例如以下内容(感谢 Holt 指出我原来的错误):

template <typename R = void, typename F,
          typename T = std::conditional_t<std::is_same_v<void, R>,
                          decltype(std::declval<F>()()), R>>
std::optional<T> handleNativeCrash(JNIEnv *env, F f) {
    try {
        return std::optional<T>(f());
    }
    catch (const std::exception &e) {
        jniThrowException(env, e.what());
        return {};
    }
}

YSC 解决方案(在尾随返回类型中使用decltype() 检测返回类型)强加函数返回的类型;使用附加(带有默认值)模板参数也允许“劫持”返回的类型来解释它。

假设您有一个 l lambda,它返回一个 int,但您想要一个 std::optional&lt;long&gt;,您可以写成这样

return my_namespace::handleNativeCrash<long>(env, l);

【讨论】:

  • 您不能将T 放在F 之前,因为T 的默认值取决于F,因此您不能在最后一个示例中仅指定long
  • @Holt - 哦!你是对的。我必须让它更复杂一点。更正;谢谢。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 2018-07-17
  • 2014-03-24
相关资源
最近更新 更多