C++ 通过传递指针地址初始化 std::unique_ptr
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12403750/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Initializing a std::unique_ptr by passing the address of the pointer
提问by Tony The Lion
I am creating a class which interops with some Windows API code, now one of the pointers I have to initialize is done by calling a native function which initializes it.
我正在创建一个与一些 Windows API 代码互操作的类,现在我必须初始化的一个指针是通过调用初始化它的本机函数来完成的。
My pointers are of type std::unique_ptr
with a custom deleter, which calls the WinAPI deleter function provided, however I cannot pass the unique_ptr with the & address-of operator to the init-function. Why?
我的指针是std::unique_ptr
带有自定义删除器的类型,它调用提供的 WinAPI 删除器函数,但是我无法将带有 & 地址运算符的 unique_ptr 传递给 init 函数。为什么?
I have created a sample that demonstrates my problem:
我创建了一个示例来演示我的问题:
#include <memory>
struct foo
{
int x;
};
struct custom_deleter {};
void init_foo(foo** init)
{
*init = new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&foo_ptr);
}
The compiler barks and says:
编译器咆哮着说:
source.cpp: In function 'int main()':
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)'
采纳答案by Steve Jessop
Somewhere under the covers, unique_ptr<foo>
has a data member of type foo*
.
在幕后的某个地方,unique_ptr<foo>
有一个类型为 的数据成员foo*
。
However, it's not legitimate for a user of the class to directly modify that data member. Doing so would not necessarily preserve the class invariants of unique_ptr
, in particular it wouldn't free the old pointer value (if any). In your special case you don't need that to happen, because the previous value is 0, but in general it should happen.
但是,该类的用户直接修改该数据成员是不合法的。这样做不一定会保留 的类不变量unique_ptr
,特别是它不会释放旧的指针值(如果有)。在您的特殊情况下,您不需要发生这种情况,因为先前的值为 0,但通常应该发生。
For that reason unique_ptr
doesn't provide access to the data member, only to a copy of its value (via get()
and operator->
). You can't get a foo**
out of your unique_ptr
.
出于这个原因unique_ptr
,不提供对数据成员的访问,只提供对其值的副本(通过get()
和operator->
)。你无法摆脱foo**
你的unique_ptr
.
You could instead write:
你可以写:
foo *tmp;
init_foo(&tmp);
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp);
This is exception-safe for the same reason that std::unique_ptr<foo, custom_deleter> foo_ptr(new foo());
is exception-safe: unique_ptr
guarantees that whatever you pass in to its constructor will eventually get deleted using the deleter.
这是异常安全的,原因与std::unique_ptr<foo, custom_deleter> foo_ptr(new foo());
异常安全相同:unique_ptr
保证您传入其构造函数的任何内容最终都会被删除器删除。
Btw, doesn't custom_deleter
need an operator()(foo*)
? Or have I missed something?
顺便说一句,custom_deleter
不需要operator()(foo*)
吗?或者我错过了什么?
回答by sbi
Steve has already explained what the technical problem is, however, the underlying problem goes much deeper: The code employs an idiom helpful when you deal with naked pointers. Why does this code do two-step initialization (first create the object, then initialize it) in the first place? Since you want to use smart pointers, I'd suggest you carefully adapt the code:
Steve 已经解释了技术问题是什么,然而,潜在的问题更深:当你处理裸指针时,代码使用了一个有用的习语。为什么这段代码首先要做两步初始化(首先创建对象,然后初始化它)?由于您想使用智能指针,我建议您仔细调整代码:
foo* init_foo()
{
return new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr( init_foo() );
}
Of course, renaming init_foo()
to create_foo()
and having it return a std::unique_ptr<foo>
directly would be better. Also, when you use two-step initialization, it's often advisable to consider using a class to wrap the data.
当然,重命名init_foo()
,以create_foo()
和有它返回一个std::unique_ptr<foo>
直接效果会更好。此外,当您使用两步初始化时,通常建议考虑使用类来包装数据。
回答by Alex Alabuzhev
You can use the following trick:
您可以使用以下技巧:
template<class T>
class ptr_setter
{
public:
ptr_setter(T& Ptr): m_Ptr{Ptr} {}
~ptr_setter() { m_Ptr.reset(m_RawPtr); }
ptr_setter(const ptr_setter&) = delete;
ptr_setter& operator=(const ptr_setter&) = delete;
auto operator&() { return &m_RawPtr; }
private:
T& m_Ptr;
typename T::pointer m_RawPtr{};
};
// Macro will not be needed with C++17 class template deduction.
// If you dislike macros (as all normal people should)
// it's possible to replace it with a helper function,
// although this would make the code a little more complex.
#define ptr_setter(ptr) ptr_setter<decltype(ptr)>(ptr)
and then:
进而:
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&ptr_setter(foo_ptr));
回答by DimanNe
I eventually came up with an approach that allows to initialise unique_ptr's with a code like this:
我最终想出了一种方法,允许使用这样的代码初始化 unique_ptr:
struct TOpenSSLDeleter { ... }; // Your custom deleter
std::unique_ptr<EVP_MD_CTX, TOpenSSLDeleter> Ctx;
...
Ctx = MakeUnique(EVP_MD_CTX_create()); // MakeUnique() accepts raw pointer
And here is the solution:
这是解决方案:
template <class X>
struct TUniquePtrInitHelper {
TUniquePtrInitHelper(X *Raw) noexcept {
m_Raw = Raw;
}
template <class T, class D>
operator std::unique_ptr<T, D>() const noexcept {
return std::unique_ptr<T, D>(m_Raw);
}
private:
X *m_Raw;
};
template <class X>
TUniquePtrInitHelper<X> MakeUnique(X *Raw) noexcept {
return {Raw};
}