英文:
How does std::unique_ptr set the other pointer to null when moving?
问题
以下是翻译好的内容:
当发生移动操作时,通常一个类将other
的指针设置为null,这也适用于std::unique_ptr
。赋值运算符被模板化,以便您可以将unique_ptr
移动到不同类型(即派生类)。
我的问题是:它如何访问另一个类中的ptr
,它是一个private
成员,鉴于它是一个实例化的模板类,因此不同,并且不能访问private
甚至protected
成员?
template <typename T>
class MyPointer
{
public:
template <typename U>
void operator=(const MyPointer<U>& other) = delete;
template <typename U>
void operator=(MyPointer<U>&& other)
{
// 不会编译通过,这是与参数不同的类
// 无法访问私有成员
other.pointer = nullptr;
}
char* get() const { return pointer; }
private:
char* pointer;
};
int main()
{
struct B {};
struct D : B {};
std::unique_ptr<B> base;
std::unique_ptr<D> derived;
// std::unique_ptr在移动时以某种方式将另一个指针设置为null
base = std::move(derived);
MyPointer<B> my_pointer_b;
MyPointer<D> my_pointer_d;
// 无法访问私有成员
my_pointer_b = std::move(my_pointer_d);
}
英文:
When a move happens, usually a class sets the other
's pointer to null, and this is the case with std::unique_ptr
. The assignment operator is templated so that you can move unique_ptr
s to different types (i.e derived classes).
My question is: How can it access the ptr
in the other class, a private
member, given that it's an instantiated templated class, and therefore different, and can't access private
or even protected
members?
template <typename T>
class MyPointer
{
public:
template <typename U>
void operator=(const MyPointer<U>& other) = delete;
template <typename U>
void operator=(MyPointer<U>&& other)
{
// WON'T COMPILE, THIS IS A DIFFERENT CLASS FROM THE ARGUMENT
// CANNOT ACCESS PRIVATE MEMBER
other.pointer = nullptr;
}
char* get() const { return pointer; }
private:
char* pointer;
};
int main()
{
struct B {};
struct D : B {};
std::unique_ptr<B> base;
std::unique_ptr<D> derived;
// std::unique_ptr somehow sets the other pointer to null when moving
base = std::move(derived);
MyPointer<B> my_pointer_b;
MyPointer<D> my_pointer_d;
// CANNOT ACCESS PRIVATE MEMBER
my_pointer_b = std::move(my_pointer_d);
}
答案1
得分: 1
Here is the translated content:
我的问题是,如何访问另一个类中的
ptr
,它是一个private
成员,鉴于它是一个实例化的模板类,因此是不同的,无法访问private
甚至protected
成员?
正如其他人提到的,您漏掉了一个明显的细节。来自 cppreference.com 的移动赋值 std::unique_ptr::operator=
解释了如何处理 std::unique_ptr<T, Deleter>
(重点在于):
unique_ptr& operator=(unique_ptr&& r) noexcept;// (1) (constexpr since C++23)
[...]
1) 移动赋值运算符。以
reset(r.release())
的方式将所有权从r
转移到*this
,然后将std::forward<Deleter>(r.get_deleter())
的值赋给get_deleter()
。[...]
您还可以重写您的 MyPointer
如下:
template <typename T, typename Deleter = std::default_delete<T>>
class MyPointer
{
public:
// ... 其他成员
template <typename U, typename OtherDeleter>
MyPointer& operator=(MyPointer<U, OtherDeleter>&& other) noexcept
{
// 就像 std::unique_ptr 一样!
reset(other.release());
deleter = std::forward<OtherDeleter>(other.get_deleter());
return *this;
}
T* release() noexcept
{
T* releasedPtr = pointer;
pointer = nullptr;
return releasedPtr;
}
void reset(T* newPtr = nullptr) noexcept
{
if (pointer != newPtr)
{
deleter(pointer);
pointer = newPtr;
}
}
template<typename Self> // 需要 C++23 支持 !!
decltype(auto) get_deleter(this Self&& self) noexcept
{
return std::forward<Self>(self).deleter;
}
private:
T* pointer{ nullptr}; // 代替 char*(也许?)
Deleter deleter;
};
† 或者,您可以将 MyPointer
类声明为 other
的 friend
。
template <typename T> class MyPointer
{
public:
// .... 其他成员!
private:
T* pointer;
// 将 MyPointer<T> 声明为 MyPointer<U> 的 friend
template <typename U>
friend class MyPointer;
};
† 这不太推荐,因为它破坏了封装的思想,可能被视为对 friend
声明的误用。
英文:
> My question is how can it access the ptr
in the other class, a private
member, given that it's an instantiated templated class, and therefore different, and can't access private
or even protected
members?
As others mentioned, you are missing the obvious detail. From the cppreference.com the move assignment std::unique_ptr::operator=
explains, how this has been handled for the std::unique_ptr<T, Deleter>
(emphasis mine):
> unique_ptr& operator=( unique_ptr&& r ) noexcept;// (1) (constexpr since C++23)
>
> [...]
>
>1) Move assignment operator. Transfers ownership from r
to *this
as if by calling reset(r.release())
followed by an assignment of get_deleter()
from std::forward<Deleter>(r.get_deleter())
.
>
>[...]
You could also rework your MyPointer
the same:
template <typename T, typename Deleter = std::default_delete<T>>
class MyPointer
{
public:
// ... other members
template <typename U, typename OtherDeleter>
MyPointer& operator=(MyPointer<U, OtherDeleter>&& other) noexcept
{
// Just like the std::unique_ptr does!
reset(other.release());
deleter = std::forward<OtherDeleter>(other.get_deleter());
return *this;
}
T* release() noexcept
{
T* releasedPtr = pointer;
pointer = nullptr;
return releasedPtr;
}
void reset(T* newPtr = nullptr) noexcept
{
if (pointer != newPtr)
{
deleter(pointer);
pointer = newPtr;
}
}
template<typename Self> // requires C++23 support !!
decltype(auto) get_deleter(this Self&& self) noexcept
{
return std::forward<Self>(self).deleter;
}
private:
T* pointer{ nullptr}; // instead of char* (maybe ?)
Deleter deleter;
};
<sup>†</sup> Alternatively, you could make the MyPointer
class be friend
of the other
.
template <typename T> class MyPointer
{
public:
// .... other members!
private:
T* pointer;
// Declare MyPointer<T> as a friend of MyPointer<U>
template <typename U>
friend class MyPointer;
};
<sup>†</sup> This is less recommended, due to the fact, it spoils the idea of encapsulation, and it can be considered as a misuse of friend
declaration.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论