繁体   English   中英

在 C++ 中为非 POD 类型创建 POD 包装器

[英]Creating a POD wrapper for non-POD types in C++

在我的代码中的某一时刻,我需要将一些参数作为 POD 结构传递(具体将数据复制到 CUDA常量memory)。 但我想通过用户定义的构造函数(无虚拟方法)传递更多“复杂”类型。

我想知道是否有任何问题(或更好的解决方案)做这样的事情来减轻 POD 约束(基本上使用 POD 结构至少与我感兴趣的 object 相同的大小作为真实事物的代理)。

#include <iostream>
#include <cstring>

// Meant to be used as a pointer to the Derived type.
template <class T>
struct PODWrapper 
{
    uint8_t data_[sizeof(T)];

    const T& operator*()  const { return *reinterpret_cast<const T*>(this); }
    const T* operator->() const { return  reinterpret_cast<const T*>(this); }
};

class NonPOD
{
    protected:

    float x_;

    public:

    NonPOD(float x) : x_(x) {}
    NonPOD(const NonPOD& other) : x_(other.x_) {}

    float  x() const { return x_; }
    float& x()       { return x_; }
};

int main()
{
    // initial value
    NonPOD initial(10.0f);

    //copying to pod wrapper
    PODWrapper<NonPOD> pod;
    std::memcpy(&pod, &initial, sizeof(NonPOD));

    // accessing pod wrapper
    NonPOD nonpod(*pod);
    std::cout << nonpod.x() << std::endl;

    return 0;
}

用例是能够声明一个结构 CUDA常量memory 具有任何类型(CUDA 期望 POD 类型)。 是这样的:

__constant__ PODWrapper<NonPOD> constantData;

我对此进行了测试,它似乎可以工作,但我特别担心 memory 问题,即使用 memcpy 到/从 PODWrapper 的“this”指针。

您的 PODWrapper 以三种方式表现出未定义的行为。 这是一个修复:

template <class T>
struct PODWrapper 
{
    alignas(T) std::byte data_[sizeof(T)];

    const T& operator*()  const { return *std::launder(reinterpret_cast<const T*>(data_)); }
    const T* operator->() const { return  std::launder(reinterpret_cast<const T*>(data_)); }
};

如果不对齐字节存储,则不能保证有足够的 memory。此外,您必须std::launder memory 地址。

但是,最大的问题是在任何地方都没有创建 object 类型(除了initial )。 memory 在那里,但就 C++ 而言,没有NonPOD object 驻留在该 memory 中。您可以使用std::construct_atstd::destory_at来创建和销毁 object。

std::construct_at(pod.data_, initial);

请注意,object 与存储 object 的 memory 不同。 特别是对于非平凡的类型(顺便说一句,POD 的概念有些过时并且不再适用)。 有关详细信息,请参阅TrivialType

不要 memcopy 到data_ 它不会创建 object 并且您仍将处于UB-land中。

访问权限对我来说很好。

当将“NonPOD”class 转换为“POD”class 非常简单时,我不确定为什么要制作包装器。

在此处查看 class 属性: https://en.cppreference.com/w/cpp/language/classes

您 class 仅缺少 2 个要求:

  • 每个符合条件的复制构造函数都是微不足道的
  • 有一个或多个符合条件的默认构造函数,因此每个构造函数都是微不足道的。

要解决这个问题,您需要删除非平凡的复制构造函数(如果需要,可以添加默认复制构造函数)并添加默认的平凡构造函数。 即这有效:

#include <iostream>
#include <type_traits>

class NonPOD
{
    protected:

    float x_;

    public:

    // Add trivial constructor
    NonPOD() = default;
    NonPOD(float x) : x_(x) {}
    // Remove non-trivial copy constructor
    //NonPOD(const NonPOD& other) : x_(other.x_) {}

    float  x() const { return x_; }
    float& x()       { return x_; }
};

int main()
{
    std::cout << std::boolalpha;
    std::cout << std::is_trivially_copyable<NonPOD>::value << std::endl;
    std::cout << std::is_standard_layout<NonPOD>::value << std::endl;
    std::cout << std::is_trivial<NonPOD>::value << std::endl;
    std::cout << std::is_pod<NonPOD>::value << std::endl;
}

Output 是:

true
true
true
true

暂无
暂无

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

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