7

我有一個'記住'一個對象的引用(例如整型變量)。我不能讓它引用一個立即遭到破壞的價值,我正在尋找一種方法來保護我的班級的用戶不會意外地這樣做。防止傳遞臨時對象

rvalue-reference overload是一種防止臨時傳入的好方法嗎?

struct HasRef { 
    int& a; 
    HasRef(int& a):a(a){} 
    void foo(){ a=1; } 
}; 


int main(){ 
    int x=5; 
    HasRef r1(x); 
    r1.foo(); // works like intended. 

    HasRef r2(x+4); 
    r2.foo(); // dereferences the temporary created by x+4 

} 

一個私人的rvalue重載嗎?

struct HasRef { 
    int& a; 
    HasRef(int& a):a(a){} 
    void foo(){ a=1; } 
private: 
    HasRef(int&& a); 
}; 

... HasRef r2(x+1); // doesn't compile => problem solved? 

有沒有我沒有看到的陷阱?

+4

臨時不結合左值參考。在第一個例子中定義'r2'不應該編譯。 – musiphil

+0

如果您使用的是VC++,一種解決方案是打開警告級別,它會告訴您什麼時候它不起作用。 –

+4

但是,_const_引用會綁定到一個臨時的,所以這個問題仍然是一個很好的問題。我已經考慮過這種方法,但我仍然認爲,如果一個類將存儲引用對象的引用(或指針),最好在構造函數中使用指針,以使潛在的生命期關注點有點更明顯(當一個構造函數接受一個指針時,通常它會讓我三次思考對象將如何處理它)。 –

回答

2

這不應該編譯。一個好的C++編譯器(或者幾乎所有我見過的C++編譯器)都會阻止這種情況的發生。

+0

謝謝;似乎我必須更好地審視我的代碼並重新解釋問題。 – xtofl

3

忽略了一個事實的代碼是無效的,只是回答有關私人超載的問題...

在C++ 11我寧願刪除功能私有函數。它有點更明確,你真的不能稱之爲(即使你是該類的成員或朋友,也不是。)

N.B.如果被刪除的構造是HasRef(int&&)=delete它不會在這裏選擇:

int i; 
HasRef hr(std::forward<const int>(i)); 

隨着類型const int&&HasRef(const int&)構造函數的參數將被使用,而不是HasRef(int&&)之一。在這種情況下,這將是確定的,因爲i確實是一個左值,但普遍認爲可能並非如此,所以這可能是非常罕見的時代之一,當一個常量右值引用是有用的:

HasRef(const int&&) = delete; 
0

我猜你正在編譯MSVS。在這種情況下,關閉語言擴展,你應該會得到一個錯誤。

否則,即使標記爲參考const也不會延長臨時的生命週期,直到構造函數完成。之後,你會引用一個無效的對象。

+0

說起來容易做起來難。使用MSVC禁用語言擴展使他們自己的標準庫抱怨。 –

+0

@AlexandreC。到目前爲止,我沒有遇到任何問題......幸運? –

+0

也許吧。我記得MSVC2005有問題。 –

2

如果你有一個const參考B類型的一些實例存儲到你的A類,那麼你一定要得到保證,A實例的那一世將B實例的生命週期超過:

B b{}; 
A a1{b}; // allowed 
A a2{B{}}; // should be denied 
B const f() { return B{}; } // const result type may make sense for user-defined types 
A a3{f()}; // should also be denied! 

爲了使它成爲可能,您應該明確地指定= delete;所有的構造函數重載,它們可以接受右值(包括const &&&&)。爲了達到這個目的,你只需要= delete;只有const &&版本的構造函數。

struct B {}; 

struct A 
{ 
    B const & b; 
    A(B const & bb) : b(bb) { ; } // accepts only `B const &` and `B &` 
    A(B const &&) = delete; // prohibits both `B &&` and `B const &&` 
}; 

此方法允許您禁止傳遞給構造函數所有類型的右值。

這也適用於內置標量。例如,double const f() { return 0.01; },雖然它造成類似的警告:

警告:在返回類型「常量」類型限定符沒有效果[-Wignored-預選賽]

還算可以,如果你有效果只是= delete;只有&&版本的構造函數:

struct A 
{ 
    double const & eps; 
    A(double const & e) : eps(e) {} // binds to `double const &`, `double &` AND ! `double const &&` 
    A(double &&) = delete; // prohibit to binding only to `double &&`, but not to `double const &&` 
}; 

double const get_eps() { return 0.01; } 

A a{0.01}; // hard error 
A a{get_eps()}; // no hard error, but it is wrong! 

對於非轉換構造函數(即非一元),有一個問題:你可能需要提供= delete; -d版本所有構造的combinatorically可能版本如下:

struct A 
{ 
    A(B const &, C const &) {} 
    A(B const &&, C const &&) = delete; 
    // and also! 
    A(B const &, C const &&) = delete; 
    A(B const &&, C const &) = delete; 
}; 

,禁止混合情況下,像:

B b{}; 
A a{b, C{}}; 
+0

好點,多參數構造函數!可能將所有參數設置爲模板是有意義的,並且靜態地聲明所有參數都是左值。這將防止混合爆炸。 – xtofl

+0

@xtofl簡單的'static_assert'不是一個好的選擇,當你將一個類型trait像'std :: is_constructible'應用到'A'時。您需要SFINAE-out不好的組合,或者如果可用,則需要使用* Concept Lite *。 – Orient