简体   繁体   English

C ++返回临时对象的问题

[英]C++ an issue with returning a temporary object

Hello I'm about a month old in C++ so please excuse me if this question is too trivial. 您好,我用C ++大约一个月大了,所以如果这个问题太琐碎,请原谅。

#include <iostream>
using namespace std;

class Point {
    int x,y;

public:
    Point(int x_val = 0,int y_val = 0) :x(x_val),y(y_val) { }
    Point(Point& copy) : x(copy.x), y(copy.y) { }

    void showLocation() const { cout << "[" << x << "," << y << "]" << endl; }
    friend Point operator+(const Point& p1, const Point& p2);
    friend Point operator-(const Point& p1, const Point& p2);

    Point operator-(Point& p2) {                    // (1)
        Point(x-p2.x, y-p2.y).showLocation();
        return Point(x-p2.x, y-p2.y);
    }
};

Point operator+(const Point& p1, const Point& p2) { // (2)
    Point pos(p1.x+p2.x, p1.y+p2.y);
    return pos;
}

Point operator-(const Point& p1, const Point& p2) { // (3)
    return Point(p1.x-p2.x, p1.y-p2.y);
}

int main() {
    Point p1(3,4);
    Point p2(2,5);

    (p1+p2).showLocation();
    //(p1-p2).showLocation();
    operator-(p1,p2);
}

So this is a simple code for practicing operator overloading - I simply created a point and overloaded + and - operators, both as a member of the class and a global function. 因此,这是一个用于练习操作符重载的简单代码-我只是创建了一个点并重载了+和-运算符,它们都作为类的成员和全局函数。

When I compile this code, however, I discovered that while (2) works, both (1) and (3) keep showing the error that I cannot see why: 但是,当我编译此代码时,我发现虽然(2)有效,但(1)和(3)都继续显示错误,我看不出为什么:

q1.cpp:17:10: error: no matching constructor for initialization of 'Point' return Point(x-p2.x, y-p2.y); q1.cpp:17:10:错误:没有匹配的构造函数来初始化'Point'返回Point(x-p2.x,y-p2.y); ^~~~~~~~~~~~~~~~~~~~~ q1.cpp:8:2: note: candidate constructor not viable: no known conversion from 'Point' to 'int' for 1st argument ^ ~~~~~~~~~~~~~~~~~~~~ q1.cpp:8:2:注意:候选构造函数不可行:第一个参数从'Point'到'int'的未知转换

As I understand it, both (1) and (3) are supposed to return a temporary Point object, which, according to Google search, should be equivalent to case (2). 据我了解,(1)和(3)均应返回一个临时Point对象,根据Google搜索,该对象应与情况(2)等效。

Also, the error statement confuses me even more - I can't see any conversion occurring in the expression mentioned as problematic. 另外,错误语句使我更加困惑-我看不到在表达式中发生的任何转换都是有问题的。

Point(x-p2.x, y-p2.y).showLocation();

This works fine, and case (2) also does, so I guess this is not a syntax issue. 这很好用,情况(2)也可以,所以我想这不是语法问题。 I couldn't see any problem other than the possibility of an issue regarding returning a temporary object (without naming it). 除了返回临时对象(未命名)的问题的可能性之外,我看不到其他任何问题。

Thanks! 谢谢!

You can fix this by simply removing your copy constructor. 您可以通过简单地删除复制构造函数来解决此问题。 It serves no useful purpose. 它没有任何用处。 Alternatively, you can fix your copy constructor by using const in the signature as per the default signature. 或者,您可以根据默认签名在签名中使用const来修复副本构造const

Point(Point const & copy) : x(copy.x), y(copy.y) { }

Without the const , the copy constructor cannot be used with a temporary instance as input. 如果没有const ,则复制构造函数不能与临时实例一起用作输入。

Resources: 资源:

This: 这个:

Point(Point& copy) : x(copy.x), y(copy.y) { }

is a technically valid copy constructor , but it restricts the possible actual arguments. 是技术上有效的副本构造函数 ,但它限制了可能的实际参数。

A temporary or const object can't be bound to the Point& formal argument, because that's a reference to non- const , which would permit modifying the actual argument. 临时或const对象不能绑定到Point&形式参数,因为这是对非const的引用,这将允许修改实际参数。

Instead you can write 相反,你可以写

Point( Point const& other ): x( other.x ), y( other.y ) {}

where I've also corrected the misleading argument naming. 在此我还纠正了误导性参数命名的问题。

But even easier, for this class you can just do 但更简单的是,对于本课程,您可以

Point( Point const& other ) = default;

And absolutely easiest, just don't define a copy constructor at all, let the compiler do it for you. 绝对最简单,就是根本不定义一个复制构造函数,让编译器为您完成。


Similarly, for maximum usability you should change 同样,为了获得最大的可用性,您应该更改

Point operator-(Point& p2) {                   // (1)

to

Point operator-(Point const& p2) const {       // (1)

The const for the argument means that it can now be called with const or temporary (rvalue) argument. 参数的const表示现在可以使用const或临时(右值)参数调用它。

And the const for the member function itself, the last const , means that it can be called on a const object. const的成员函数本身,最后一个const ,意味着它可以在一个被称为const对象。 The rules here are a bit subtle. 这里的规则有些微妙。 In contrast to the restriction for the argument, without the const it can already be called on a temporary object (ie via an rvalue-expression). 与参数的限制相反,如果没有const则可以在临时对象上调用它(即通过rvalue-expression)。


General advice: 一般建议:

  • sprinkle const liberally just about everywhere it can be used, 在几乎可以使用的任何地方自由地散布const

  • let the compiler generate copy operations unless you're taking charge of copying , and 让编译器生成复制操作, 除非您负责复制 ,并且

  • preferentially use data member of types where copying is safe, eg built-in types, smart pointers and standard library collections. 优先使用安全复制类型的数据成员,例如内置类型,智能指针和标准库集合。

The reason for the error (in your case (1)) is 错误的原因(在您的情况下为(1))是

Point operator-(Point& p2) {                    // (1)
    Point(x-p2.x, y-p2.y).showLocation();
    return Point(x-p2.x, y-p2.y);
}

is that the Point(x-p2.x, y - p2.y) produces a temporary object. Point(x-p2.x, y - p2.y)产生一个临时对象。 Returning that (semantically) requires creating a copy of the temporary, which requires a copy constructor that accepts a const argument. 返回(语义上)需要创建临时副本,这需要接受const参数的副本构造const Your copy constructor does not accept a const argument. 您的副本构造函数不接受const参数。

The solutions are to either modify your copy constructor to accept a const parameter, or remove it completely (since the compiler will automatically generate a copy constructor that works correctly for you). 解决方案是修改您的副本构造函数以接受const参数,或者将其彻底删除(因为编译器会自动生成一个对您而言正确工作的副本构造函数)。 In C++11, use Point(const Point &) = default; 在C ++ 11中,使用Point(const Point &) = default; if needed, to force the compiler to generate the correct copy constructor. 如果需要,可以强制编译器生成正确的副本构造函数。

Note: the standard requires the compiler to enforce the need for the correct copy constructor, even if the temporary is optimised out of existance. 注意:该标准要求编译器强制执行对正确的副本构造函数的需求,即使临时文件已被优化为不存在也是如此。 This helps improve portability of code to compilers that do not optimise out the temporary. 这有助于提高代码对未优化临时文件的编译器的可移植性。

Another concern with your code is that you have two variants of operator-() . 代码的另一个问题是您有两种operator-()变体。

Your cases (1) and (3) are not equivalent. 您的情况(1)和(3)不相等。 The first generates a member function form of operator-() (which accepts one argument, since there is an implied this ) and the second is a non-member form which accepts two arguments. 第一个生成operator-()的成员函数形式(接受一个参数,因为有一个隐含this ),第二个生成的非成员形式接受两个参数。

When it sees an expression like p1-p2 (where p1 and p2 are both of type Point ), both versions of that operator-() are equally good candidates. 当它看到一个类似p1-p2的表达式(其中p1p2都是Point类型)时,该operator-()两个版本都是不错的候选者。 The compiler will complain about ambiguity, since it has no reason to prefer one over the other. 编译器会抱怨模棱两可,因为没有理由比另一个更偏爱一个。 So a statement 所以一个声明

(p1-p2).showLocation();

will not compile. 将无法编译。 I assume you have commented it out for that reason. 我认为您是出于这个原因将其注释掉了。

The solution to that is to use one form or the other of operator-() . 解决方案是使用operator-()一种形式或另一种形式。 Not both at once. 不能一次全部。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM