Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 从函数正确传播'decltype(auto)`变量_C++_C++17_Auto_Copy Elision_Decltype Auto - Fatal编程技术网

C++ 从函数正确传播'decltype(auto)`变量

C++ 从函数正确传播'decltype(auto)`变量,c++,c++17,auto,copy-elision,decltype-auto,C++,C++17,Auto,Copy Elision,Decltype Auto,(这是“”的后续内容) 考虑以下场景-我想将一个函数f传递给另一个函数invoke\u log\u return,该函数将: 调用f 将某物打印到标准输出 返回f的结果,避免不必要的复制/移动并允许复制省略 注意,如果f抛出,则不应向标准输出任何内容。这就是我到目前为止所做的: template <typename F> decltype(auto) invoke_log_return(F&& f) { decltype(auto) result{std::f

(这是“”的后续内容)

考虑以下场景-我想将一个函数
f
传递给另一个函数
invoke\u log\u return
,该函数将:

  • 调用
    f

  • 将某物打印到标准输出

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

  • 注意,如果
    f
    抛出,则不应向标准输出任何内容。这就是我到目前为止所做的:

    template <typename F>
    decltype(auto) invoke_log_return(F&& f)
    {
        decltype(auto) result{std::forward<F>(f)()};
        std::printf("    ...logging here...\n");
    
        if constexpr(std::is_reference_v<decltype(result)>)
        {
            return decltype(result)(result);
        }
        else
        {
            return result;
        }
    }
    
    虽然
    invoke\u log\u return\u scope
    要短得多,但这需要函数行为的不同心理模型和新抽象的实现。令人惊讶的是,
    g++
    clang++
    都使用此解决方案执行RVO/拷贝省略


    如所述,这种方法的一个主要缺点是,
    f
    的返回值不能作为日志消息的一部分。

    我们可以使用修改后的
    std::forward
    :(避免使用forward名称以防止ADL问题)

    这样,如果
    std::forward(f)(
    返回

    • prvalue,则
      结果
      为非引用,而
      调用日志返回
      返回非引用类型

    • 一个左值,然后
      result
      是一个左值引用,
      invoke\u log\u return
      返回一个左值引用类型

    • 一个xvalue,然后
      result
      是一个右值引用,
      invoke\u log\u return
      返回一个右值引用类型


    (基本上是抄袭自我的)

    这是最简单、最清晰的书写方式:

    template <typename F>
    auto invoke_log_return(F&& f)
    { 
        auto result = f();
        std::printf("    ...logging here... %s\n", result.foo());    
        return result;
    }
    
    因此,如果代码清晰,具有相同的功能,但没有像竞争对手那样优化以运行,那么这是编译器优化失败,clang应该解决这个问题。这类问题在工具中解决比在应用层实现中解决更有意义

    Q1:“这是从函数中“完美”返回decltype(auto)变量的最短可能的方法吗?有没有更简单的方法来实现我想要的?” 好吧,证明最优性总是很困难的,但是你的第一个解决方案已经很短了。实际上,您唯一希望删除的是
    if constexpr
    ——其他一切都是必要的(不改变问题的重点)

    您的第二个解决方案解决了这一问题,但要付出一些额外的精神扭曲和无法在log语句中使用变量的代价——或者更一般地说,它只允许您执行与结果无关的操作

    @david kennedy的简单解决方案通过创建一个prvalue,然后将其复制到最终存储位置,从而以一种简洁的方式解决了这个问题。如果您的用例支持此模型,并且您使用GCC,那么它几乎是最好的解决方案:

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

    但是,可以创建一个在GCC和clang上执行零复制/移动操作的解决方案,方法是去掉
    if constexpr
    ,并将不同的实现移动到两个函数中,这两个函数通过
    enable\u if
    进行区分:

    模板
    自动调用日志返回(F&&F)
    ->std::如果启用,则启用<
    标准::是参考值吗,
    decltype(std::forward(f)()
    >
    {
    decltype(auto)结果{std::forward(f)()};
    std::printf(“…日志记录glvalue…\n”);
    返回decltype(result)(result);
    }
    模板
    自动调用日志返回(F&&F)
    ->std::如果启用,则启用<
    !标准::是参考值吗,
    decltype(std::forward(f)()
    >
    {
    decltype(auto)结果{std::forward(f)()};
    std::printf(“…记录prvalue…\n”);
    返回结果;
    }
    
    零份:

        s()
    in main
    
    prvalue
        s()
        ...logging prvalue...
    
    lvalue
        ...logging glvalue...
    
    xvalue
        ...logging glvalue...
    

    现在,当然,与原始解决方案相比,这增加了行数,尽管它返回的变量可以说“更完美”(在这个意义上,NRVO由两个编译器执行)。将功能提取到实用程序函数中会引出第二个问题

    问题2:“能否将
    if constexpr{…}else{…}
    模式提取到单独的函数中?提取它的唯一方法似乎是宏。” 不可以,因为不能省略将PR值传递到函数中,这意味着将
    result
    传递到函数中会导致复制/移动。对于glvalues,这不是问题(如
    std::forward
    所示)

    但是,可以稍微更改先前解决方案的控制流,以便将其本身用作库函数:

    template <typename F>
    decltype(auto) invoke_log_return(F&& f) {
        return invoke_return(std::forward<F>(f), [](auto&& s) {
            std::printf("    ...logging value at %p...", static_cast<void*>(&s));
        });
    }
    
    问题3:“对于上述prvalue案例,clang++没有执行NRVO有什么好的理由吗?应该将其报告为潜在的增强,还是g++的NRVO优化在这里不合法?” 检查我的C++2a草案(N4835§11.10.5/1.1[class.copy.elision]),NRVO的表述非常简单:

    • 当表达式是非易失性[check]自动[check]对象的名称时,在类返回类型为[check]的函数[check]中的
      return
      语句[check]中[function template实例化为返回
      s
      ,so check](除了*handler*(14.4)[check]的异常删除引入的函数参数或变量)与函数返回类型[check]具有相同的类型(忽略cv限定),可以通过将自动对象直接构造到函数调用的返回对象中来省略复制/移动操作

    我不知道这应该是无效的任何其他原因。

    on\u scope\u success
    也处理无效的返回类型。decltype(result)(result)不是有点多余吗?返回(result)不是这样做的。返回类型为decltype(auto)的函数一个带有括号内值的return语句会自动返回一个引用。on-scope success变量的缺点是无法记录返回值!对我来说,更有趣的任务是
        s()
    
    in main
    
    prvalue
        s()
        ...logging here... Foo!
    
    lvalue
        s(const s&)
        ...logging here... Foo!
    
    xvalue
        s(s&&)
        ...logging here... Foo!
    
    template <typename F>
    auto invoke_log_return(F&& f)
    { 
        auto result = f();
        std::printf("    ...logging here...\n");    
        return result;
    }
    
        s()
    in main
    
    prvalue
        s()
        ...logging here...
    
    lvalue
        s(const s&)
        ...logging here...
    
    xvalue
        s(s&&)
        ...logging here...
    
        s()
    in main
    
    prvalue
        s()
        ...logging prvalue...
    
    lvalue
        ...logging glvalue...
    
    xvalue
        ...logging glvalue...
    
    template <typename F>
    decltype(auto) invoke_log_return(F&& f) {
        return invoke_return(std::forward<F>(f), [](auto&& s) {
            std::printf("    ...logging value at %p...", static_cast<void*>(&s));
        });
    }
    
    template <typename F, typename G>
    auto invoke_return(F&& f, G&& g)
        -> std::enable_if_t<
            std::is_reference_v<decltype(std::forward<F>(f)())>,
            decltype(std::forward<F>(f)())
        >
    {
        decltype(auto) result{std::forward<F>(f)()};
        std::forward<G>(g)(decltype(result)(result));
        return decltype(result)(result);
    }
    
    template <typename F, typename G>
    auto invoke_return(F&& f, G&& g)
        -> std::enable_if_t<
            !std::is_reference_v<decltype(std::forward<F>(f)())>,
            decltype(std::forward<F>(f)())
        >
    {
        decltype(auto) result{std::forward<F>(f)()};
        std::forward<G>(g)(result);
        return result;
    }