union不能被初始化由于有 non-trivial构造函数

背景

最近遇到一个问题,当定义一个union的时候,发现初始化失败,具体如下:

class NonTrivial {
public:
    NonTrivial(const std::string& s) : data(s) {
        std::cout << "NonTrivial constructed with " << data << std::endl;
    }
     NonTrivial()  {
        std::cout << "NonTrivial constructed default" << data << std::endl;
    }
    ~NonTrivial() {
        std::cout << "NonTrivial destroyed with " << data << std::endl;
    }
    void Show() const {
        std::cout << "Showing " << data << std::endl;
    }
private:
    std::string data;
};
union MyUnion {
    int i;
    double d;
    NonTrivial nt;
//    MyUnion(int x) : i(x) {}
//    MyUnion(double y) : d(y) {}
//    MyUnion(const std::string& s) : nt(s) {}
//    ~MyUnion(){};
};
int main()
{
	MyUnion test; // compiler error
}

结果:
在这里插入图片描述

从报错可以看出,MyUnion()的构造函数被删了…, 之前在这里讨论过编译器何时会生成默认的构造函数,按理说符合自动生成默认构造函数,为啥MyUnion()会被删掉了呢?这里涉及到一个规则:

union 中,如果包含非平凡成员(no-trivial),编译器无法为union自动生成合适的构造函数、析构函数或赋值操作符

那么问题来了,什么才是非平凡成员

非平凡成员

在 C++ 中,“非平凡成员”通常指的是具有复杂构造、析构或赋值行为的成员。以下是一些常见的非平凡成员特征:

  1. 自定义构造函数
    如果类定义了任何自定义构造函数(包括有参数的构造函数),该类的构造函数是非平凡的。自定义构造函数会影响如何初始化对象。

  2. 自定义析构函数
    如果类定义了自定义析构函数,则该类的析构函数是非平凡的。析构函数用于释放类对象占用的资源。

  3. 自定义赋值操作符
    如果类定义了自定义赋值操作符(包括拷贝赋值和移动赋值),则该类的赋值操作符是非平凡的。赋值操作符用于将一个对象的值赋给另一个对象。

  4. 包含成员对象(也称为成员子对象)
    如果类的成员是具有上述非平凡构造函数、析构函数或赋值操作符的类,则该成员也被认为是非平凡的。换句话说,如果一个类包含非平凡成员对象,那么这个类的构造函数、析构函数或赋值操作符也将是非平凡的。

  5. 有虚函数
    如果类中定义了虚函数(即 virtual 函数),则这个类的析构函数是非平凡的,因为需要处理虚表(vtable)相关的资源释放。

  6. 有虚基类
    如果一个类从虚基类继承,那么这个类的构造函数、析构函数和赋值操作符也会变成非平凡的,因为需要处理虚基类的初始化和清理。

根据这个规则基本可以看出,由于NonTrivial中含有string, 而string包含非平凡构造函数,所以nt是个非平凡成员

如何解决

  • 手动定义Myunion的构造和析构函数,试用于小项目
  • 当项目用的较多的时候,不适合给每个union写构造函数,毕竟使用union主要是利用其共用一块内存的特点,这时可以利用包裹器进行类型准换,初始化的时候避开Union, 而实际使用的时候再转Union, 下面式cub中实现的一个,我觉得还不错,大家可以参考使用。

template <typename T>
struct Uninitialized
{
    /// Biggest memory-access word that T is a whole multiple of and is not larger than the alignment of T
    typedef char DeviceWord;
    enum
    {
        WORDS = sizeof(T) / sizeof(DeviceWord)
    };
    /// Backing storage
    DeviceWord storage[WORDS];
    /// Alias
     T* Ptr()
    {
        return reinterpret_cast<T*>(this);
    }
     T& Alias()
    {
        return reinterpret_cast<T&>(*this);
    }
};
struct TempStorage : Uninitialized<MyUnion> {};

int main()
{
	TempStorage test; // 初始化提前到这里
    MyUnion& a = test.Alias();
    MyUnion* p = test.Ptr();
}

思考

其实在union中的成员函数nt, 虽然是非平凡成员,但是构造函数和析构函数都有,为啥union不能直接调用相关函数生成一个默认的构造函数和析构函数呢?不是很明白有多难。。。

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值