英文:
std::any implementation details
问题
观察std::any
的实现,发现通过aligned_storage
访问小对象时使用了const
,这非常奇怪。这是为了处理aligned_storage
可能存储const
对象的情况吗?如果是这样,标准在哪里支持这一点?
最新的std::any
实现链接在libstdc++中:
static _Tp* _S_access(const _Storage& __storage)
{
// The contained object is in __storage._M_buffer
const void* __addr = &__storage._M_buffer;
return static_cast<_Tp*>(const_cast<void*>(__addr));
}
期待您的想法。
英文:
Looking at std::any
implementation it is very strange why access through aligned_storage
for small objects is implemented with const
. What is the purpose of this? It is to do with something that aligned_storage
may store const
objects? If so where does the standard backs this up?
Link to the latest std::any
implementation in libstdc++
static _Tp* _S_access(const _Storage& __storage)
{
// The contained object is in __storage._M_buffer
const void* __addr = &__storage._M_buffer;
return static_cast<_Tp*>(const_cast<void*>(__addr));
}
Appreciate any thoughts.
答案1
得分: 2
这里没有什么诀窍,只是简单的实现方式-避免代码重复。
你需要同时拥有const
和非const
版本的这个函数,它们的实现基本相同。很容易只编写一个版本,并将const正确性的负担放在用户身上-这是一个内部函数,所以用户是库的实现者,他们知道自己在做什么。
对于静态分配和动态分配的对象,_S_access()
都通过const&
接受__storage
,这样你可以传递const _Storage&
和_Storage&
两种类型。
对于静态分配的对象:
static _Tp*
_S_access(const _Storage& __storage)
{
// 所包含的对象在__storage._M_buffer中
const void* __addr = &__storage._M_buffer;
return static_cast<_Tp*>(const_cast<void*>(__addr));
}
当获取_Storage
的数据成员_M_buffer
的地址时,它会自动获得const
限定符,因此我们需要将其转换掉。
对于动态分配的对象:
static _Tp*
_S_access(const _Storage& __storage)
{
// 所包含的对象在*__storage._M_ptr中
return static_cast<_Tp*>(__storage._M_ptr);
}
我们读取类型为void*
的_M_ptr
数据成员。没有const
传播到该void*
中,因此不需要使用const_cast
。const正确性仍然由用户负责。
英文:
There is no trick here, it's just simplicity of implementation - to avoid code duplication.
You need both const
and non-const
versions of this function with essentially the same implementation. It's easy to write only one version and put const-correctness burden on a user - this is an internal function, so that user is a library implementer and they know what they are doing.
For both statically and dynamically allocated objects _S_access()
takes __storage
by const&
, so that you could pass both const _Storage&
and _Storage&
.
For statically allocated objects,
static _Tp*
_S_access(const _Storage& __storage)
{
// The contained object is in __storage._M_buffer
const void* __addr = &__storage._M_buffer;
return static_cast<_Tp*>(const_cast<void*>(__addr));
}
when an address of _Storage
's data member _M_buffer
is taken, it automatically gets const
qualification, so we need to cast it away.
For dynamically allocated ones,
static _Tp*
_S_access(const _Storage& __storage)
{
// The contained object is in *__storage._M_ptr
return static_cast<_Tp*>(__storage._M_ptr);
}
we read _M_ptr
data member of type void*
. No const
is propagated into that void*
, so no const_cast
is needed. Const-correctness is still on a user.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论