std::any实现细节

huangapple go评论99阅读模式
英文:

std::any implementation details

问题

观察std::any的实现,发现通过aligned_storage访问小对象时使用了const,这非常奇怪。这是为了处理aligned_storage可能存储const对象的情况吗?如果是这样,标准在哪里支持这一点?

最新的std::any实现链接在libstdc++中:

https://github.com/gcc-mirror/gcc/blob/a90bd3ea6d1ba27b15476f0a768d7952c6723420/libstdc%2B%2B-v3/include/std/any#L392

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++

https://github.com/gcc-mirror/gcc/blob/a90bd3ea6d1ba27b15476f0a768d7952c6723420/libstdc%2B%2B-v3/include/std/any#L392

static _Tp* _S_access(const _Storage&amp; __storage)
{ 
   // The contained object is in __storage._M_buffer
  const void* __addr = &amp;__storage._M_buffer;
  return static_cast&lt;_Tp*&gt;(const_cast&lt;void*&gt;(__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&amp;, so that you could pass both const _Storage&amp; and _Storage&amp;.

For statically allocated objects,

static _Tp*
_S_access(const _Storage&amp; __storage)
{
  // The contained object is in __storage._M_buffer
  const void* __addr = &amp;__storage._M_buffer;
  return static_cast&lt;_Tp*&gt;(const_cast&lt;void*&gt;(__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&amp; __storage)
{
  // The contained object is in *__storage._M_ptr
  return static_cast&lt;_Tp*&gt;(__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.

huangapple
  • 本文由 发表于 2023年8月9日 04:33:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76863037.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定