完美转发和临时对象的范围

时间:2015-01-05 13:50:10

标签: c++ c++11 scope move

为了实现*运算符的完美转发,我构建了以下示例。

#include <string>
#include <iostream>

class A {
public:
    std::string name;
    A(const A& _other)  : name(_other.name) {
        std::cout << "Copy-Construct with name: " << name << std::endl;
    }
    A(A&& _other)       : name(std::move(_other.name)) {
        std::cout << "Move-Construct with name: " << name << std::endl;
    }
    A(std::string _name): name(_name) { }
};

A operator*(const A& _lhs, const A& _rhs) {
    std::cout << "Start Operator Copy with: " << _lhs.name << " " << _rhs.name << std::endl;
    A bla(_lhs.name+" "+_rhs.name);
    return bla;
}

A&& operator*(A&& _lhs, const A& _rhs) {
    std::cout << "Start Operator Move with: " << _lhs.name << " " << _rhs.name << std::endl;
    _lhs.name += " "+_rhs.name;
    return std::move(_lhs);
}

int main() {
    A a("a");
    A b("b");
    A c("c");
    A d("d");

    A x = a*b*A("t1")*c*A("t2")*A("t3")*d; 

    std::cout << "Final result is: " << x.name << std::endl;
}

结果正如我所希望的那样,特别是只有一个移动构造函数而没有调用复制构造函数。

Start Operator Copy with: a b
Start Operator Move with: a b t1
Start Operator Move with: a b t1 c
Start Operator Move with: a b t1 c t2
Start Operator Move with: a b t1 c t2 t3
Start Operator Move with: a b t1 c t2 t3 d
Move-Construct with name: a b t1 c t2 t3 d
Final result is: a b t1 c t2 t3 d

现在我的问题是:这个代码是C++11吗?特别是我可以依赖这样一个事实:第一个临时对象(由a和b构成)在分号处留下它的范围而不是在它之前?并且是构造,将作为移动参考获得的对象作为移动参考返回,合法吗?

1 个答案:

答案 0 :(得分:3)

A&& operator*(const A& _lhs, const A& _rhs) {
    std::cout << "Start Operator Copy with: " << _lhs.name << " " << _rhs.name << std::endl;
    A* bla = new A(_lhs.name+" "+_rhs.name);
    return std::move(*bla);
}

这会创建一个动态分配的对象,因此调用者负责删除它。你的例子没有这样做,所以泄漏了内存。这是一个糟糕的功能。它应该通过值返回,这将更快,因为您没有在堆上分配对象。

A&& operator*(A&& _lhs, const A& _rhs) {
    std::cout << "Start Operator Move with: " << _lhs.name << " " << _rhs.name << std::endl;
    _lhs.name += " "+_rhs.name;
    return std::move(_lhs);
}

这不会导致内存泄漏,所以不像第一个那样完全明显错误,但它仍然是错误的。如果你用一个临时对象来调用它,它会返回对同一个临时对象的引用,但这可能导致悬空引用:

A&& c = A("a") * A("b");

引用c绑定到A("a")创建的临时值,但在语句末尾超出范围。任何使用c的尝试都有未定义的行为。

两个重载都应该按值返回。

对于左侧是左值并且右侧是右值的情况,您可能还需要重载,因为这样可以重新使用右侧对象。如果你添加,你还需要一个重载,两个操作数都是rvalues。基本上,请查看std::string定义operator+

的方式