这个C++ref示例有问题吗?
我在检查aligned_storage中cppref,但我认为它的例子是越野车。这是代码:
#include <iostream>
#include <type_traits>
#include <string>
template<class T, std::size_t N>
class static_vector
{
// properly aligned uninitialized storage for N T's
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
std::size_t m_size = 0;
public:
// Create an object in aligned storage
template<typename ...Args> void emplace_back(Args&&... args)
{
if( m_size >= N ) // possible error handling
throw std::bad_alloc{};
// construct value in memory of aligned storage
// using inplace operator new
new(&data[m_size]) T(std::forward<Args>(args)...);
++m_size;
}
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// note: needs std::launder as of C++17
return *reinterpret_cast<const T*>(&data[pos]);
}
// Delete objects from aligned storage
~static_vector()
{
for(std::size_t pos = 0; pos < m_size; ++pos) {
// note: needs std::launder as of C++17
reinterpret_cast<T*>(&data[pos])->~T();
}
}
};
int main()
{
static_vector<std::string, 10> v1;
v1.emplace_back(5, '*');
v1.emplace_back(10, '*');
std::cout << v1[0] << 'n' << v1[1] << 'n';
}
在这里,我们要创建一个使用放置 new的静态向量。问题是typename std::aligned_storage<sizeof(T), alignof(T)>::type类型是 POD,而不是T. 所以我们需要在使用前投射它。我认为代码应该是这样的:
#include <iostream>
#include <type_traits>
#include <string>
template<class T, std::size_t N>
class static_vector
{
// properly aligned uninitialized storage for N T's
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
T* data_ptr = reinterpret_cast<T*>(data);
std::size_t m_size = 0;
public:
// Create an object in aligned storage
template<typename ...Args> void emplace_back(Args&&... args)
{
if( m_size >= N ) // possible error handling
throw std::bad_alloc{};
// construct value in memory of aligned storage
// using inplace operator new
new(&data_ptr[m_size]) T(std::forward<Args>(args)...);
++m_size;
}
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// note: needs std::launder as of C++17
return *reinterpret_cast<const T*>(&data_ptr[pos]);
}
// Delete objects from aligned storage
~static_vector()
{
for(std::size_t pos = 0; pos < m_size; ++pos) {
// note: needs std::launder as of C++17
reinterpret_cast<T*>(&data_ptr[pos])->~T();
}
}
};
int main()
{
static_vector<std::string, 10> v1;
v1.emplace_back(5, '*');
v1.emplace_back(10, '*');
std::cout << v1[0] << 'n' << v1[1] << 'n';
}
我对吗?虽然我不知道为什么原始代码在 clang 中有效。
更新:
让我说得更具体一点。按照标准,aligned_storage类型是 POD 而不是T。所以它的实现可以如下:
template<std::size_t Len, std::size_t Align /* default alignment not implemented */>
struct aligned_storage {
struct type {
alignas(Align) unsigned char data[Len];
};
};
现在,如果您使用 访问它data[pos],地址将根据unsigned charsize=1增加,不是sizeof(T)吗?
回答
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
你似乎认为这&data[i]会返回这个数组中第 i 个字节的地址,而实际上它会返回i * sizeof(std::aligned_storage<sizeof(T), alignof(T)>)第 th 个字节的地址,它与i * sizeof(T)第 th 个字节相同。例子。
OP:问题是在
aligned_storagedata 中分配了例如unsigned charnot typeT。因此,当您使用&a[i]它时,假设每个成员的大小都不是1(指针是unsigned char)访问成员sizeof(T)。
这不是它的工作原理。aligned_storage在引擎盖下使用什么类型unsigned char[N]或其他东西并不重要。
为简洁起见,让我们使用using A = std::aligned_storage<sizeof(T), alignof(T)>::type;.
该数组的data类型为A[N]。当您对其应用运算符[]时,它会衰减为类型为 的指针A *。
data[i]相当于*(data + i)。将整数添加到指针时,整数将乘以sizeof指向类型的整数。尖头类型是A,因此sizeof(A)使用(等于sizeof(T))。