kof*_*fes 7 c++ smart-pointers memory-alignment language-lawyer c++17
我试图创建一个智能指针,它只有一个指向内存块的指针,该指针以引用计数器(控制块)开头,并在其后立即存储一个值。在从论坛、标准和 cppreference 中阅读了一些内容后,我意识到代码看起来充满了 UB。
#include <iostream>
#include <memory>
#include <type_traits>
#include <new>
struct alignas(alignof(size_t)) StorageBase {
size_t m_rc = 0;
};
template<typename T>
struct Storage: public StorageBase {
using value_type = T;
std::aligned_storage_t<sizeof(value_type), alignof(value_type)> m_value_storage;
};
class Dummy {
public:
Dummy() { std:: cout << "Dummy constructed" << std::endl; }
virtual ~Dummy() { std:: cout << "Dummy destructed" << std::endl; }
};
class DerivedDummy: public Dummy {
public:
DerivedDummy() { std:: cout << "DerivedDummy constructed" << std::endl; }
~DerivedDummy() override { std:: cout << "DerivedDummy destructed" << std::endl; }
};
int main(int argc, char const *argv[]) {
using first_storage_type = Storage<DerivedDummy>;
auto* first_storage = new first_storage_type;
++first_storage->m_rc;
new (&first_storage->m_value_storage) first_storage_type::value_type();
StorageBase* storage = first_storage;
using second_storage_type = Storage<Dummy>;
if constexpr (std::is_convertible_v<first_storage_type::value_type*, second_storage_type::value_type*>) {
using second_value_type = second_storage_type::value_type;
// UB ?
auto* second_storage = static_cast<second_storage_type*>(storage);
++second_storage->m_rc;
// UB ?
std::launder<second_value_type>(
static_cast<second_value_type*>(
static_cast<void*>(
&second_storage->m_value_storage
)
)
)->~second_value_type();
delete second_storage;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
因为我知道存在解决方案,您可以在其中添加指向StorageBase类的指针aligned_storage<...>::type,它会正常工作。
在阅读了有关对齐和标准布局的更多信息后,我将代码重构为新的代码。
#include <iostream>
#include <memory>
#include <type_traits>
#include <new>
// explicit alignment here...
struct alignas(size_t) StorageBase {
size_t m_rc = 0;
};
template<typename T>
// ... and here
struct alignas(StorageBase) Storage: public StorageBase {
using value_type = T;
std::aligned_storage_t<sizeof(value_type), alignof(value_type)> m_value_storage;
};
class Dummy {
public:
Dummy() { std:: cout << "Dummy constructed" << std::endl; }
virtual ~Dummy() { std:: cout << "Dummy destructed" << std::endl; }
};
class DerivedDummy: public Dummy {
public:
DerivedDummy() { std:: cout << "DerivedDummy constructed" << std::endl; }
~DerivedDummy() override { std:: cout << "DerivedDummy destructed" << std::endl; }
};
int main(int argc, char const *argv[]) {
using first_storage_type = Storage<DerivedDummy>;
auto* first_storage = new first_storage_type;
++first_storage->m_rc;
const auto ptr_to_storage = new (&first_storage->m_value_storage) first_storage_type::value_type();
StorageBase* storage = first_storage;
using second_storage_type = Storage<Dummy>;
if constexpr (std::is_convertible_v<first_storage_type::value_type*, second_storage_type::value_type*>) {
using second_value_type = second_storage_type::value_type;
/*
* Storage<T> is explicitly aligned as StorageBase,
* but Storage<T> is not standard layout
*/
second_value_type* ptr = reinterpret_cast<second_value_type*>(storage + 1); // UB?
++storage->m_rc;
std::launder<second_value_type>(ptr)->~second_value_type();
::operator delete(storage);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
197 次 |
| 最近记录: |