我应该如何使用这些参数来确保返回值优化?

问题描述 投票:0回答:1

我想模仿 Rust 如何在 C++ 中进行错误处理,并且我知道使用 std::move 可以抑制 RVO,并且可能(我不确定)使用 r 值引用。我想知道下面应该如何将构造函数参数传递给 Result 类:

enum ResultEnum : unsigned char { Ok, Err };


template <typename T, typename E = bool>
struct Result
{
private:
    ResultEnum result;
public:
    Result(ResultEnum result, const T& t) /* SHOULD I TAKE THE ARGUMENT BY COPY, REFERENCE OR FORWARDING/UNIVERSAL REFERENCE */
    {
        this->t = t; /* WHAT WOULD BE BEST HERE? std::move???*/
        this->result = result;
    }
    Result(ResultEnum result, const E& err) /* SAME HERE, COPY, REF OR FORWARDING/UNIVERSAL REF? */
    {
        this->e = err; /* WHAT WOULD BE BEST HERE? std::move??? Will std::move prevent RVO?*/

        this->result = result;
    }
    union
    {
        T t;
        E e;
    };

    bool is_ok() const{ return result == ResultEnum::Ok; }
    bool is_err() const{ return result == ResultEnum::Err; }

    
};

struct MyFoo {};

Result<MyFoo, bool> createMyFoo()
{
    MyFoo f;
    return { Ok, f }; /* CAN I EXPECT RVO HERE? WHAT EFFECT WOULD CHANGING THE CONSTRUCTOR ARGUMENTS MAKE, IE., COPY vs REF vs FORWARDING/UNIVERSAL REF */
}

int main()
{
    auto f = createMyFoo();
    if (f.is_err())
    {
        /* SOMETHING */
    }
}

在构造函数内部,假设发生 RVO,我可以假设构造函数内部的任何赋值都是直接赋值给调用堆栈帧中的返回值吗?

c++ return-value-optimization
1个回答
0
投票

如果你有一个左值,其类型与函数的返回类型不同,那么编译器能做的最好的事情就是将其移至结果的构造函数中,它无法对该值执行 RVO,因为它的类型与返回值的类型不同,讨论此问题的一个很好的视频是 C++ Weekly - Ep 421 - You're using 可选、变体、对、元组、任意和预期错误!

在此函数中,创建了 2 个

MyFoo
,一个在函数内,一个在
Result
内,
Result
的返回是自 C++17 以来强制的 RVO

Result<MyFoo, bool> createMyFoo()
{
    MyFoo f; // f already materialized !
    return { Ok, f }; // f is moved into `Result`, MyFoo&& works but see better option
 // return { Ok, std::move(f) } this is equivalent, don't add std::move yourself
}

要解决这个问题,您不需要创建

f
,最常见的方法是使用一个构造函数,该构造函数接受
Args&&...args
并将其转发到
MyFoo
的构造函数中,这样我们就可以延迟
MyFoo
的具体化,如下所示尽可能多。

class Ok_t {};
constexpr Ok_t Ok; // make the enum in question an enum class

struct Result
{
...

template <typename...Args>
Result(Ok_t, Args&&...args) 
{
    // use placement new for C++17
    std::construct_at(&t,std::forward<Args>(args)...); 
    this->result = ResultEnum::Ok;
}
...
}

现在可以通过这种方式调用这个构造函数了

Result<MyFoo, bool> createMyFoo()
{
    return { Ok }; // mandatory RVO since C++17
}

并且如果

MyFoo
需要任何参数,它可以将其传递到该构造函数中,这样只有
MyFoo
内部结果被创建,下一个是非强制性的 NRVO。 (命名为RVO)

Result<MyFoo, bool> createMyFoo()
{
    Result<MyFoo, bool> result{ Ok };
    return result; // non-mandatory NRVO
}

下一个片段仍然创建 2 个 MyFoo,但它将第一个移动到第二个

Result<MyFoo, bool> createMyFoo()
{
    MyFoo f; // f already materialized !
    return { Ok, f }; // f is moved into `Result`
}

RVO 的完整示例

© www.soinside.com 2019 - 2024. All rights reserved.