2016-02-27 70 views
1

你好,我在C++大約一個月的時間,所以請原諒,如果這個問題太微不足道了。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); 
} 

因此,這是實踐操作符重載一個簡單的代碼 - 我只是創造了一個點和過載+和 - 運營商,無論是作爲類的成員,全局函數。

當我編譯這段代碼,但是,我發現,雖然(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:8:2: note: candidate constructor not viable: no known conversion from 'Point' to 'int' for 1st argument

據我瞭解,(1)和(3)都應該返回一個臨時的Point對象,根據Google搜索,它應該等同於情況(2)。

此外,錯誤陳述更令我困惑 - 我看不到任何轉換髮生在提到的表達式中是有問題的。

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

這工作得很好,和外殼(2)也做,所以我想這不是一個語法問題。除了關於返回臨時對象(不命名它)的問題的可能性之外,我看不到任何問題。

謝謝!

+0

我可以看到你來自哪裏。爲什麼(2)與(1)和(3)不同?從概念上說,它們基本上是等價的,但從技術上講,因爲(2)使用了一個命名變量而不是臨時的,它允許該值基於非const引用參數。 C++不允許以這種方式使用臨時對象。 – nobar

+0

因此,我在網上看到的'返回SomeObject(1,2)'實際上是錯誤的,那麼。猜猜我必須做更好的搜索。謝謝! – SJC

+0

這沒有錯。問題出在你的拷貝構造函數中。 – nobar

回答

1

你可以通過簡單地刪除你的拷貝構造函數來解決這個問題。它沒有用處。或者,您可以按照默認簽名在簽名中使用const來修復您的拷貝構造函數。

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

沒有const,拷貝構造函數不能用一個臨時的實例作爲輸入使用。

資源:

+0

謝謝,無論是答案和資源! – SJC

0

此:

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

是技術上有效拷貝構造函數,但是它限制了可能的實際申辯發言:。

臨時對象或const對象不能綁定到Point&形式參數,因爲這是對非const的引用,它允許修改實際參數。

相反,你可以寫

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

在那裏我還糾正了誤導性的說法命名。

但更容易,這個類你可以做

Point(Point const& other) = default; 

而且絕對最簡單的,只是沒有定義拷貝構造函數的一切,讓編譯器爲你做它。


同樣,對於最大的可用性,你應該改變

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

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

const的說法意味着它現在可以const或臨時(右值)參數來調用。

而成員函數本身的const最後的const意味着它可以在const對象上調用。這裏的規則有點微妙。與對參數的限制相反,如果沒有const,它可以在臨時對象上調用(即通過右值表達式)。


一般建議:

  • const寬鬆只是到處可以使用,

  • 讓編譯器生成拷貝操作除非你採取複製充電,和

  • 優先使用wh類型的數據成員複製是安全的,例如內置類型,智能指針和標準庫集合。

0

的原因的錯誤(在你的情況(1))是

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

在於Point(x-p2.x, y - p2.y)產生臨時對象。返回(語義)需要創建臨時副本,這需要一個接受const參數的拷貝構造函數。您的拷貝構造函數不接受const參數。

解決方法是修改您的拷貝構造函數以接受const參數,或者將其完全移除(因爲編譯器會自動生成一個能夠正確工作的拷貝構造函數)。在C++ 11中,如果需要,使用Point(const Point &) = default;來強制編譯器生成正確的拷貝構造函數。

注:該標準要求編譯器強制執行正確拷貝構造函數的需要,即使臨時優化不存在。這有助於提高代碼的可移植性,從而不會優化臨時代碼。

您的代碼的另一個問題是您有兩個operator-()變體。

您的案例(1)和(3)並不等同。第一個生成operator-()(它接受一個參數,因爲暗含this)的成員函數格式,第二個是接受兩個參數的非成員形式。

當它看到像p1-p2(其中p1p2都是Point類型的)的表達,這是operator-()的兩個版本都同樣良好的候選者。編譯器會抱怨含混不清,因爲它沒有理由相互偏好。所以發表聲明

(p1-p2).showLocation(); 

不會編譯。我假設你已經爲此評論過它。

解決方案是使用operator-()的一種形式或另一種形式。不一次。