2016-05-27 166 views
5

我目前正在嘗試調用sqlite3庫函數,它期望我通過它sqlite3**獲取指向智能指針指向的對象的指針 - Ivalue錯誤

這是我目前的代碼。我有一個工作部分,一部分是給我一個錯誤:

sqlite3 *sqlite = m_db.get(); 
if (sqlite3_open(std::string(m_dbName.begin(), m_dbName.end()).c_str(), &sqlite)) 
{ 

} 

if (sqlite3_open(std::string(m_dbName.begin(), m_dbName.end()).c_str(), &(m_db.get()))) 
{ 

} 

m_db場看起來是這樣的:

std::unique_ptr<sqlite3> m_db = nullptr; 

兩個例子我展示中,第一個是工作完全正常。然而,第二個給我這個錯誤。請注意,這是從&(m_db.get())部分來:

「Address expression must be an lvalue or a function designator」 

我讀了關於左值和右值一點點,但我似乎無法找出爲什麼這個語法是不可能的。據我現在瞭解,問題是.get()操作的返回值僅僅是一個臨時表達式結果,因此在內存中沒有可以從中獲取地址的可識別位置。

我想,在一個聲明中必須有一種方法來實現這一點。

任何人都可以向我解釋爲什麼這不起作用,我怎麼可能解決它?

+0

你爲什麼與'm_dbName' –

+0

做怪異的東西,這是一個wstring的,我轉換成字符串 – Sossenbinder

+0

這是相當狡猾 –

回答

8

&運算符只能與左值一起使用(或在指向成員時使用限定的ID)。表達式m_db.get()是一個右值,因爲它按值返回一個指針,而不是引用,所以你不能接收它的地址。

unique_ptr沒有提供訪問底層指針的方法作爲參考,你需要在第一個例子中存儲一個副本。

+0

而它不會讓你訪問底層指針的原因是'unique_ptr'必須做一些工作,如果你分配一個新的值(具體來說,調用刪除舊的值)。如果你只是進入封面並分配給底層的指針,它就無法完成這項工作。 –

1

There has to be a way to achieve this in one statement, I guess.

我對此不太確定;關於臨時價值的觀點確實可能是它需要一個聲明來獲得永久性聲明。

此外,你正在搞亂智能指針的語義,你不應該這樣做 - .get真的不應該在這裏使用。

Soooo,我要做的就是依靠這裏的C++範圍,並且不關心我先聲明一個「普通」指針以便稍後創建一個智能指針的事實。

your_class::initialize_db() { 
    sqlite3 *sqlite; 
    int retval = sqlite3_open(std::string(m_dbName.begin(), m_dbName.end()).c_str(), &sqlite); 
    if(retval == SQLITE_OK) 
     m_db = std::unique_ptr<sqlite3>(sqlite); 

} 
+0

請注意,即使打開失敗,sqlite也應該關閉:'「打開時是否發生錯誤,當數據庫連接句柄關聯的資源不再需要時,應通過傳遞給sqlite3_close()來釋放資源。」 ',所以應該不管創建unique_ptr。 (但它也需要一個自定義刪除器,如其他地方提到的那樣。) – Zitrax

3

智能指針存儲指針並將其返回get。你想在這裏做的是相反的:你從sqlite3_open得到一個指針並且想把它存儲在一個智能指針中。所以,你會做這樣的事情

sqlite3* db = nullptr; 
sqlite3_open(..., &db); 
m_db.reset(db); 

由於unique_ptr的主要特徵是刪除其析構函數中包含指針,我不知道這是否有道理這裏使用它。據我瞭解,你應該在返回的指針上調用sqlite3_close,而不是刪除它。

+1

您可以給[定製刪除器](http://stackoverflow.com/questions/19053351/how-do-i-use-a-custom-deleter-with- a-stdunique-ptr-member)添加到'unique_ptr'。這將調用一個不同的函數而不是'delete'。 (但是,如你所說,最好不要在這裏使用unique_ptr)。 –

+0

是的,的確如此。一旦我發現它如何與sqlite3一起工作,我已經改變了這一點。然而,儘管如此,我仍然想問問題,但從來不會因爲學習超出需要的東西而感到痛苦。 – Sossenbinder

1

左值基本上可以出現在賦值運算符的左側。所以錯誤說你只能檢索可以分配給某個東西的地址,或者一個函數。如果你有權訪問unique_ptr中的sqlite3 *指針,但是你沒有,並且有很好的理由,它會起作用的。

更重要的是,在這種情況下,您不應該使用智能指針。如果sqlite3_open需要sqlite3 **參數,則表示該函數將爲sqlite3 *指針提供一個值。基本上它是一個out參數形式的C#或其他這樣的語言。將它作爲函數結果提供將會更清楚,但結果代碼已將其除去。這一切都很好,但智能指針想要控制這個值。您在初始化時設置一次該值,但在此之後,智能指針會處理它。爲了維護它的約束,它需要做到這一點:所有權的唯一性,指針本身超出範圍時的解除分配等。如果你基本上去覆蓋裏面的sqlite3 *指針,那就不會再發生了,因爲智能指針無法攔截覆蓋並釋放當前正在使用的對象。