获取由智能指针指向的对象的指针 - Ivalue错误

5

我目前正在尝试调用sqlite3库函数,它希望我传递一个sqlite3**

这是我的当前代码。有一部分可以工作,还有一部分给我一个错误:

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()操作的返回值只是一个临时表达式结果,因此没有一个可识别的内存位置,我可以从中获取地址。
我猜必须有一种方法可以在一个语句中实现这一点。
有人可以向我解释一下为什么这不起作用,以及我如何可能修复它吗?

你为什么要用 m_dbName 做奇怪的事情? - M.M
这是一个我正在将wstring转换为string的过程。 - Sossenbinder
那很靠不住。 - M.M
@M.M 是的,我刚刚查了一下还能用什么,我选择了使用boost lexical cast。 - Sossenbinder
4个回答

8
&运算符只能与左值(或在创建成员指针时使用限定符id)一起使用。表达式m_db.get()是一个rvalue,因为它通过值返回指针而不是引用,所以你不能获取其地址。 unique_ptr没有提供将基础指针作为引用访问的方法,您需要像第一个示例中那样将其存储在某个位置上。

而且它不允许您访问底层指针的原因是,如果您分配一个新值(具体来说,调用旧值上的删除器),unique_ptr必须执行一些工作。如果您只是在内部进行操作并将其分配给底层指针,它无法完成这项工作。 - Steve Jessop

3

智能指针存储一个指针并在获取时返回它。在这里,您想要做的相反:从sqlite3_open获取指针,并希望将其存储在智能指针中。因此,您可以采取类似以下方式:

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

作为unique_ptr的主要特点是在其析构函数中删除所包含的指针,我不确定在这里使用它是否有意义。据我理解,您应该调用sqlite3_close来关闭返回的指针,而不是删除它。

1
你可以给 unique_ptr 提供自定义删除器,以便调用不同于 delete 的函数。(但在这种情况下最好不要使用 unique_ptr)。 - M.M
是的,没错。我一发现它在sqlite3中的工作方式后就立即进行了更改。尽管如此,我仍想提出这个问题,学习多一点总是有益的。 - Sossenbinder

1
一个lvalue基本上是指可以出现在赋值运算符左侧的东西。因此,错误表明您只能检索可以分配给它或函数的地址。如果您可以访问unique_ptr内部的sqlite3*指针成员,则可以工作,但您没有这样做,有充分的理由。
更重要的是,在这种情况下不应使用智能指针。如果sqlite3_open需要sqlite3**参数,则意味着该函数将为sqlite3*指针提供值。基本上,它是来自C#或其他类似语言的“out”参数形式。如果它作为函数结果提供,那么它会更清晰,但结果代码已经取走了它。这一切都很好,但智能指针想要控制这个值。您在初始化时设置一次值,但之后,智能指针会处理它。它需要这样做以维护其约束:所有权的唯一性,在指针本身超出范围时进行解除分配等。如果您基本上覆盖内部的sqlite3*指针,那么这就不再可能发生,因为智能指针无法拦截覆盖并释放它当前正在使用的对象。

1

我想必须有一种方法可以在一个语句中实现这个,我猜。

我不太确定; 临时值的重点可能是需要一个语句才能得到永久值。

此外,您正在操纵智能指针的语义,这是不应该做的-在这里真的不应该使用.get

所以,我会在这里依赖于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);

}

请注意,即使打开失败,sqlite也应该被关闭:“在不管何时发生错误时,都应该通过将数据库连接句柄传递给sqlite3_close()来释放与之关联的资源,当它不再需要时。” 因此,应该创建unique_ptr ,无论是否打开成功。 (但是,如其他地方所述,它还需要自定义删除器。) - Zitrax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接