这个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 type T。因此,当您使用&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))。


以上是这个C++ref示例有问题吗?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>