英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论