从函数中正确传播 `decltype(auto)` 变量 [英] Correctly propagating a `decltype(auto)` variable from a function

查看:39
本文介绍了从函数中正确传播 `decltype(auto)` 变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(这是 "`decltype(auto)` 变量是否有任何实际用例?")

(This is a follow-up from "Are there any realistic use cases for `decltype(auto)` variables?")

考虑以下场景 - 我想将一个函数 f 传递给另一个函数 invoke_log_return 它将:

Consider the following scenario - I want to pass a function f to another function invoke_log_return which will:

  1. 调用f;

打印一些东西到stdout;

返回f的结果,避免不必要的复制/移动并允许复制省略.

Return the result of f, avoiding unnecessary copies/moves and allowing copy elision.

请注意,如果 f 抛出,则不应向 stdout 打印任何内容.这是我目前所拥有的:

Note that, if f throws, nothing should be printed to stdout. This is what I have so far:

template <typename F>
decltype(auto) invoke_log_return(F&& f)
{
    decltype(auto) result{std::forward<F>(f)()};
    std::printf("    ...logging here...
");

    if constexpr(std::is_reference_v<decltype(result)>)
    {
        return decltype(result)(result);
    }
    else
    {
        return result;
    }
}

让我们考虑各种可能性:

Let's consider the various possibilities:

  • f 返回一个 prvalue 时:

  • result 将是一个对象;

invoke_log_return(f) 将是一个 prvalue(符合复制省略).

invoke_log_return(f) will be a prvalue (eligible for copy elision).

f 返回一个 lvaluexvalue 时:

When f returns an lvalue or xvalue:

  • result 将作为参考;

invoke_log_return(f) 将是 lvaluexvalue.

您可以在这里在godbolt.org上看到一个测试应用程序.如您所见,g++prvalue 情况执行 NRVO,而 clang++ 不会.

You can see a test application here on godbolt.org. As you can see, g++ performs NRVO for the prvalue case, while clang++ doesn't.

问题:

  • 这是从函数中完美"返回 decltype(auto) 变量的最短方法吗? 有没有更简单的方法来实现什么我想要?

  • Is this the shortest possible way of "perfectly" returning a decltype(auto) variable out of a function? Is there a simpler way to achieve what I want?

是否可以将 if constexpr { ... } else { ... } 模式提取到单独的函数中? 提取它的唯一方法似乎成为一个宏.

Can the if constexpr { ... } else { ... } pattern be extracted to a separate function? The only way to extract it seems to be a macro.

clang++ 对上述 prvalue 情况不执行 NRVO 有什么好的理由吗? 是否应该将其报告为潜在的增强,还是 g++ 的 NRVO 优化在这里不合法?

Is there any good reason why clang++ does not perform NRVO for the prvalue case above? Should it be reported as a potential enhancement, or is g++'s NRVO optimization not legal here?

这是使用 on_scope_success 助手的替代方法(如 Barry Revzin 所建议):

Here's an alternative using a on_scope_success helper (as suggested by Barry Revzin):

template <typename F>
struct on_scope_success : F
{
    int _uncaught{std::uncaught_exceptions()};

    on_scope_success(F&& f) : F{std::forward<F>(f)} { }

    ~on_scope_success()
    {
        if(_uncaught == std::uncaught_exceptions()) {
            (*this)();
        }
    }
};

template <typename F>
decltype(auto) invoke_log_return_scope(F&& f)
{
    on_scope_success _{[]{ std::printf("    ...logging here...
"); }};
    return std::forward<F>(f)();
}

虽然 invoke_log_return_scope 更短,但这需要函数行为的不同心理模型和新抽象的实现.令人惊讶的是,g++clang++ 都使用此解决方案执行 RVO/复制省略.

While invoke_log_return_scope is much shorter, this requires a different mental model of the function behavior and the implementation of a new abstraction. Surprisingly, both g++ and clang++ perform RVO/copy-elision with this solution.

godbolt.org 上的实例

正如 Ben Voigt 所述,这种方法的一个主要缺点是 f 不能是日志消息的一部分.

One major drawback of this approach, as mentioned by Ben Voigt, is that the return value of f cannot be part of the log message.

推荐答案

这是最简单明了的写法:

That's the simplest and most clear way to write it:

template <typename F>
auto invoke_log_return(F&& f)
{ 
    auto result = f();
    std::printf("    ...logging here... %s
", result.foo());    
    return result;
}

海湾合作委员会得到了正确的(没有不必要的复制或移动)预期结果:

The GCC gets the right (no needless copies or moves) expected result:

    s()

in main

prvalue
    s()
    ...logging here... Foo!

lvalue
    s(const s&)
    ...logging here... Foo!

xvalue
    s(s&&)
    ...logging here... Foo!

因此,如果代码清晰,具有相同的功能,但没有像竞争对手那样优化运行,这是编译器优化失败,clang 应该解决这个问题.这种问题在工具中解决更有意义,而不是在应用层实现中解决.

So if code is clear, have ever the same functionality but is't optimized to run as much as the competitors does it's a compiler optimization failure and clang should work it out. That's the kind of problem that make lot more sense solved in the tool instead the application layer implementation.

https://gcc.godbolt.org/z/50u-hT

这篇关于从函数中正确传播 `decltype(auto)` 变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆