为什么union不能用于继承?

时间:2010-09-01 04:13:48

标签: c++

我在c ++标准(§9.5/ 1)中看到了以下内容:

  

联盟不得有基类。工会不得   用作基类。

     

联盟可以拥有成员函数   (包括建设者和   析构函数),但不是虚拟的(10.3)   功能

从上面来看,union也可以有构造函数和析构函数。

那么为什么在继承中不允许这样做?

编辑:回答评论:

  1. 如果允许union作为基类,则派生类可以使用其数据。如果派生类有兴趣只使用union的一个成员,则这种方式可用于保存内存。我认为,这是不正当的继承。在这种情况下,在派生类中使用union更好吗?

  2. 如果允许union作为派生类,它可以使用基类服务。例如,如果Union有多种类型的数据。众所周知,只能使用一种数据。对于每种类型的数据,存在基类以为该特定类型提供服务。在这种情况下,可以使用多重继承来获取Union中所有类型数据的所有基类的服务。这也让我觉得继承的使用不当。但是,在这一点上是否有任何相同的概念来实现内容?

  3. 只是我的想法......

3 个答案:

答案 0 :(得分:18)

这是一个简单的解决方法:

struct InheritedUnion
{
    union {
        type1 member1;
        type2 member2;
    };
};

struct InheritsUnion : InheritedUnion
{};

通过使联合匿名,它就像基本类型实际上是一个联合一样。

答案 1 :(得分:12)

答案 2 :(得分:1)

Ben Voigt的解决方法只有在您不需要继承类成员时才有效。如果由于某种原因,您需要多个基类来共享相同的继承数据,并且还想继承成员函数,那么这是一种方法,无需任何额外的空间/时间成本:

#include <iostream>
#include <type_traits>

namespace detail {

    // If you use Boost, you can use use boost::copy_cv instead
    template<class T, class U>
    struct copy_cv {

        using const_copied = std::conditional_t<
            std::is_const<U>{}, std::add_const_t<T>, T>;

        using type = std::conditional_t<
            std::is_volatile<U>{},
            std::add_volatile_t<const_copied>,
            const_copied>;
    };

    // a derived class uses this template to share data between bases
    template<typename Data, typename Derived>
    struct storage {

        template<typename BasePtr>
        static inline constexpr decltype(auto)
        get(BasePtr&& ptr) {

            // enforcing cv-qualifiers from the BasePtr
            using qualified_base = 
                std::remove_reference_t<std::remove_pointer_t<BasePtr>>;

            using qualified_derived =
                typename copy_cv<Derived, qualified_base>::type;

            using qualified_data =
                typename copy_cv<Data, qualified_base>::type;

            // casting the base "this" pointer to the base class with data
            return static_cast<qualified_data*>(static_cast<qualified_derived*>(ptr));
        }
    };
}

// the base class templates here ending with "_impl" have no data, and are
// EBO-ed away. They ultimately uses data from a different base class
template<typename Data>
struct print_impl {

    void print() const {
        std::cout << Data::get(this)->number << '\n';
    }
};

 // add_impl_1 and add_impl_2 supply "overloaded" member functions for the derived class
template<typename Data>
struct add_impl_1 {

    int add(int i) const {
        return Data::get(this)->number + i;
    }
};

template<typename Data>
struct add_impl_2 {

    template<int i>
    int add() const {
        return Data::get(this)->number + i;
    }
};

// this is the base class containing data
struct bar_data {

    int number = 42;
};

struct bar :

    // derived class inherits the data class
    bar_data,

    // using the storage template, we give the "implementation"
    // base classes access to the data
    print_impl<detail::storage<bar_data, bar>>,
    add_impl_1<detail::storage<bar_data, bar>>,
    add_impl_2<detail::storage<bar_data, bar>> {

    // using declarations are necessary to disambiguate the "overloads"
    using add_impl_1<detail::storage<bar_data, bar>>::add;
    using add_impl_2<detail::storage<bar_data, bar>>::add;
};

static_assert(sizeof(bar_data) == sizeof(bar), "");

int main() {

    bar b{};

    b.print();
    std::cout << b.add(1) << std::endl;
    std::cout << b.add<2>() << std::endl;
}

据我所知,这只适用于一些非常具体的元编程场景,或者可能是一些面向方面的编程。