简体   繁体   English

这个 C++ ref 示例有问题吗?

[英]Is this C++ ref example buggy?

I was checking aligned_storage in cppref , but I think its example is buggy.我正在检查aligned_storage中的aligned_storage ,但我认为它的例子有问题。 Here is the code:这是代码:

#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';
}

Here we want to create a static vector that uses placement new .在这里,我们要创建一个使用放置 new的静态向量。 The problem is that typename std::aligned_storage<sizeof(T), alignof(T)>::type type is POD, not T .问题是typename std::aligned_storage<sizeof(T), alignof(T)>::type type 是 POD,而不是T so we need to cast it before using.所以我们需要在使用前投射它。 I think code should be something like this:我认为代码应该是这样的:

#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';
}

Am I right?我对吗? Although I don't know why original code works in clang.虽然我不知道为什么原始代码在 clang 中有效。

Update:更新:

Let me be more specific.让我说得更具体一点。 By standard, aligned_storage type is POD not T .按照标准, aligned_storage类型是 POD 而不是T so its implementation can be as following:所以它的实现可以如下:

template<std::size_t Len, std::size_t Align /* default alignment not implemented */>
struct aligned_storage {
    struct type {
        alignas(Align) unsigned char data[Len];
    };
};

Now if you access this with data[pos] , address will be increased based on unsigned char size=1, not sizeof(T) isn't it?现在,如果您使用data[pos]访问它,地址将根据unsigned char size=1 增加,而不是sizeof(T)不是吗?

typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];

You seem to think that &data[i] will return the address of i-th byte in this array, while in reality it will return the address of i * sizeof(std::aligned_storage<sizeof(T), alignof(T)>) th byte, which is the same as i * sizeof(T) th byte.你似乎认为&data[i]会返回这个数组中第 i 个字节的地址,而实际上它会返回i * sizeof(std::aligned_storage<sizeof(T), alignof(T)>) th 字节,与i * sizeof(T) th 字节相同。 Example.例子。

OP: The problem is that in aligned_storage data is allocated in for example unsigned char not type T . OP:问题在于,在aligned_storage数据被分配在例如unsigned char而不是类型T so when you are using &a[i] it access members assuming each member size is 1 (pointer was unsigned char ) not sizeof(T) .因此,当您使用&a[i]它访问成员,假设每个成员大小为1 (指针为unsigned char )而不是sizeof(T)

This is not how it works.这不是它的工作原理。 It doesn't matter what type aligned_storage uses under the hood, unsigned char[N] or something else.什么类型的aligned_storage在引擎盖下使用什么类型, unsigned char[N]或其他东西都没有关系。

For brevity, let's use using A = std::aligned_storage<sizeof(T), alignof(T)>::type;为简洁起见,让我们使用using A = std::aligned_storage<sizeof(T), alignof(T)>::type; . .

The array data has type A[N] .数组data类型为A[N] When you apply operator [] to it, it decays to a pointer of type A * .当您对其应用 operator []时,它会衰减为A *类型的指针。

data[i] is equivalent to *(data + i) . data[i]等价于*(data + i) When adding an integer to a pointer, the integer is multiplied by sizeof of the pointed type.将整数添加到指针时,整数乘以sizeof类型的sizeof The pointed type is A , so sizeof(A) is used (which is equal to sizeof(T) ).尖头类型是A ,因此使用sizeof(A) (等于sizeof(T) )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM