首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何让编译器忽略这个值为false的if-constexpr?

如何让编译器忽略这个值为false的if-constexpr?
EN

Stack Overflow用户
提问于 2019-09-17 21:45:49
回答 3查看 414关注 0票数 3

我正在编写一个宏来包装函数调用,比如:macro(function()),返回一个std::optional<T>,其中T是函数的返回值。到目前为止,它还可以工作,但是当函数没有返回类型(即返回std::optional<bool> )时,当我试图让它返回一个空值时,我遇到了问题。

问题的核心是,当函数没有返回类型时,编译器试图计算if-constexpr的作用域,该作用域的计算结果为false。

我尝试使用两个if-constexprs和一个带有else的。我尝试内联所有内容,并将其展开为几行代码。我尝试用常量文字替换编译器计算的常量。到目前为止,一切都不起作用。

我现在拥有的宏看起来是这样的:

代码语言:javascript
复制
#ifndef troy
#define troy(body)[](){\
    constexpr auto inner_type_is_void=std::is_same<decltype(body), void>::value;\
    using inner_type=std::conditional<inner_type_is_void,bool,decltype(body)>::type;\
    try{\
        if constexpr (inner_type_is_void) {(body); return std::optional<inner_type>{true};}\
        if constexpr (!inner_type_is_void) {return std::optional<inner_type>{(body)};}\
    } catch (...){\
        return std::optional<inner_type>{};\
    }\
}()
#endif

我是这样使用它的:

代码语言:javascript
复制
{
    auto a=troy(f());
    if (!a){
        std::cout << "fail" << std::endl;
    } else{
        std::cout << "success: " << *a << std::endl;
    }
}

当f返回值时,它会起作用,但如果不返回值,则会给出编译器错误。这个编译器错误是:no matching function for call to ‘std::optional<bool>::optional(<brace-enclosed initializer list>)’在if-constexpr计算结果为false的行上。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-09-17 22:03:49

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

一如既往,解决方案就是将所有东西都包装在一个lambda中。现在,您正在将body内联用于实际的函数体。相反,您可以使用lambda:

代码语言:javascript
复制
[&]() -> decltype(auto) { return body; }

然后将整个代码封装在一个立即调用的lambda中,该lambda将这个lambda作为参数:

代码语言:javascript
复制
[](auto f){
    // stuff
}([&]() -> decltype(auto) { return body; })

现在,我标记了// stuff,我们在一个模板上下文中,我们可以实际使用if constexpr来避免实例化:

代码语言:javascript
复制
[](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; })

现在只需添加一堆\,就可以开始了。

票数 6
EN

Stack Overflow用户

发布于 2019-09-17 22:09:58

您可以在没有宏的情况下完成此操作(这是我推荐的):

代码语言:javascript
复制
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};
        }

    }
}
代码语言:javascript
复制
auto foo1() {}
auto foo2() { return 24; }

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

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

代码语言:javascript
复制
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); });
}
票数 1
EN

Stack Overflow用户

发布于 2019-09-17 22:11:02

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

因为没有依赖类型(它不是模板),所以编译器可以检查每个分支的错误。

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

依赖表达式是不同的。只有在没有类型会使分支编译的情况下,分支是无效的,程序才是病态的。所以仍然不能使用未声明的符号,但肯定会有一个类型使std::optional<inner_type>{(body)}有效,所以编译器不会实例化那个分支。

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

代码语言:javascript
复制
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

Live example

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57975669

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档