3
4

3 回答 3

6

为了避免对 进行实例化if constexpr,您需要一个实际具有实例化的上下文:您需要某种模板。if constexpr在模板之外,没有赋予的魔力。

与往常一样,解决方案是将所有内容包装在 lambda 中。现在,您正在body对实际的函数体使用 in-line。相反,您可以使用 lambda:

[&]() -> decltype(auto) { return body; }

然后将整个内容包装在一个立即调用的 lambda 中,该 lambda 将此 lambda 作为参数:

[](auto f){
    // stuff
}([&]() -> decltype(auto) { return body; })

现在,在我标记的地方// stuff,我们处于模板上下文中,我们实际上可以使用它if constexpr来避免实例化:

[](auto f){
    using F = decltype(f);
    using R = std::invoke_result_t<F>;
    try {
        if constexpr (std::is_void_v<R>>) {
            f();
            return std::optional<bool>(true);
        } else {
            return std::make_optional(f());
        }
    } catch (...) {
        return std::optional<std::conditional_t<std::is_void_v<R>, bool, R>>();
    }
}([&]() -> decltype(auto) { return body; })

现在只需添加一堆\s 就可以了。

于 2019-09-17T14:03:49.080 回答
1

您可以在没有宏的情况下执行此操作(我推荐):

template <class F>
constexpr auto troy(F f)
{
    using R = decltype(f());
    static_assert(!std::is_reference_v<R>);

    if constexpr (std::is_void_v<R>)
    {
        try
        {
            f();  
        }
        catch(...)
        {
        }
        return std::optional<bool>{std::nullopt};
    }
    else
    {
        try
        {
            return std::optional<R>(f());
        }
        catch(...)
        {
            return std::optional<R>{std::nullopt};
        }

    }
}
auto foo1() {}
auto foo2() { return 24; }

auto test()
{
    auto a = troy(foo1);
    auto b = troy(foo2);
}

如果你有一个重载的函数,有一个简单的修复:

auto foo(int a) -> void;
auto foo(int a, int b) -> void;


auto test()
{
    // your version:
    // auto f = troy(foo(1024, 11));

    // my version:
    auto f = troy([] { return foo(1024, 11); });
}
于 2019-09-17T14:09:58.130 回答
1

编译器将尽其所能在 if 的错误分支中查找错误。

由于没有依赖类型(它不是模板),因此编译器可以检查每个分支是否存在错误。

因此无效使用 void 表达式,使用未声明的符号和其他类似的东西在任何分支中都无效,无论是否为 constexpr。

依赖表达式不同。只有当分支无效且没有类型会使分支编译时,程序才会形成错误。因此,仍然不能使用未声明的符号,但肯定会有一种类型可以使std::optional<inner_type>{(body)}之有效,因此编译器根本不会实例化该分支。

这可以用函数模板实现,然后你的宏用 lambda 调用它:

template<typename T>
auto try_impl(T closure) noexcept {
    using body_type = decltype(closure());
    constexpr auto inner_type_is_void = std::is_void_v<body_type>;
    using inner_type = std::conditional_t<inner_type_is_void, bool, body_type>;

    try {
        if constexpr (std::is_void_v<body_type>) {
            closure();
            return std::optional<inner_type>{true};
        } else {
            return std::optional<inner_type>{closure()};
        }
    } catch(...) {
        return std::optional<inner_type>{};
    }
}

#ifndef troy
#define troy(body) try_impl([&]{ return body; })
#endif

活生生的例子

于 2019-09-17T14:11:02.283 回答