2

如何在最后 3 个运算符中强制执行 RVO:

#include <iostream>

class Noisy {
    private:
        int m_value;
    public:
        Noisy(int value = 0): m_value(value) 
        {
            std::cout << "Noisy(int)\n";
        }
        Noisy(const Noisy& other): m_value(other.m_value)  
        { 
            std::cout << "Noisy(const Noisy&)\n";
        }
        Noisy(Noisy&& other): m_value(other.m_value)
        { 
            std::cout << "Noisy(Noisy&&)\n";
        }
        //~Noisy() {
        //    std::cout << "dtor\n";
        //}
        Noisy operator+(const Noisy& rhs) &
        {
            std::cout << "+(const Noisy&)&\n";
            return Noisy(m_value + rhs.m_value);
        }
        Noisy operator+(Noisy&& rhs) &
        {
            std::cout << "+(Noisy&&)&\n";
            rhs.m_value += m_value;
            return rhs; //std::move(rhs);
        }
        Noisy operator+(const Noisy& rhs) &&
        {
            std::cout << "+(const Noisy&) &&\n";
            this->m_value += rhs.m_value;
            return *this; //std::move(*this);
        }

        Noisy operator+(Noisy&& rhs) &&
        {
            std::cout << "+(Noisy&&) &&\n";
            this->m_value += rhs.m_value;
            return *this; //std::move(*this);
        }
};

int main()
{
    Noisy a, b, c, d, e, f, g;
    Noisy z = a + b + c + d + e + f + g;

    return 0;
}

程序运行输出:

Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&)
+(const Noisy&) &&
Noisy(const Noisy&) 

或者std::move在最后三个运算符中显式使用时:

Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) && 
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&)
+(const Noisy&) &&
Noisy(Noisy&&) 

我不想在运算符中复制,如下所示:

Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
Noisy(int)
+(const Noisy&)&
Noisy(int)
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&
+(const Noisy&) &&

到目前为止,我想出的唯一方法是从操作员那里返回一个引用,但这显然会导致一个悬空引用。

我用新鲜的 g++ 在 c++14 和 c++17 中编译。

更新

我知道在不违反规则的情况下,不可能强制编译器做我想做的事。
但是编译器在本地优化右值的 ptevents 是什么?我想它可以在第一次添加期间创建一个右值,在下一次添加中修改该右值,然后分配给结果变量。

4

1 回答 1

0

简答

使您的 rvalue-reference-caller + const-reference 参数运算符返回一个std::move*this.

Noisy&& operator+(const Noisy& rhs) &&
{
    std::cout << "+(const Noisy&)&&\n";
    m_value += rhs.m_value;
    return std::move(*this);
}

由于这是从右值引用中触发的,因此您可以自由滥用并移动*this到调用者,在那里他们可以自由地做任何他们喜欢的事情。

例子

#include <iostream>

class Noisy {
private:
    int m_value;
public:
    Noisy(int value = 0) : m_value(value)
    {
        std::cout << "Noisy()\n";
    }
    Noisy(const Noisy& other) : m_value(other.m_value)
    {
        std::cout << "Noisy(const Noisy&)\n";
    }
    Noisy(Noisy&& other) : m_value(other.m_value)
    {
        std::cout << "Noisy(Noisy&&)\n";
        other.m_value = -1;
    }

    ~Noisy()
    {
        std::cout << "~Noisy() : " << m_value << '\n';
    }

    Noisy operator+(const Noisy& rhs) const &
    {
        std::cout << "+(const Noisy&)&\n";
        return Noisy(m_value + rhs.m_value);
    }

    Noisy&& operator+(const Noisy& rhs) &&
    {
        std::cout << "+(const Noisy&)&&\n";
        m_value += rhs.m_value;
        return std::move(*this);
    }

    Noisy operator+(Noisy&& rhs) const
    {
        std::cout << "+(Noisy&&)\n";
        rhs.m_value += m_value;
        return std::move(rhs);
    }
};

int main()
{
    Noisy a, b, c, d, e, f, g;
    std::cout << "========================\n";

    Noisy z = a + b + c + d + e + f + g;
    std::cout << "========================\n";

    return 0;
}

输出

Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
Noisy()
========================
+(const Noisy&)&
Noisy()
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
+(const Noisy&)&&
Noisy(Noisy&&)
~Noisy() : -1
========================
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0
~Noisy() : 0

请注意,在 的初始构造之后a.operator+(b),生成的对象通过右值引用沿链传播。最后一个操作是移动构造 into z。(因此-1在析构函数报告中)。


我已经准备了一个关于您的操作员链接如何使我要发布的工作的长篇描述,但最后它变得相当混乱。可以这么说:

Noisy z = a.operator+(b).operator+(c).operator+(....

就是您正在做的事情,您需要获取(b)链中过去所有内容的 lhs 操作数,以将其右值引用推送到下一个调用。我展示的操作员将允许这样做。

祝你好运,我希望我能理解你的目标。

于 2019-11-28T11:48:19.733 回答