我在c ++标准(§9.5/ 1)中看到了以下内容:
联盟不得有基类。工会不得 用作基类。
联盟可以拥有成员函数 (包括建设者和 析构函数),但不是虚拟的(10.3) 功能
从上面来看,union也可以有构造函数和析构函数。
那么为什么在继承中不允许这样做?
编辑:回答评论:
如果允许union作为基类,则派生类可以使用其数据。如果派生类有兴趣只使用union的一个成员,则这种方式可用于保存内存。我认为,这是不正当的继承。在这种情况下,在派生类中使用union更好吗?
如果允许union作为派生类,它可以使用基类服务。例如,如果Union有多种类型的数据。众所周知,只能使用一种数据。对于每种类型的数据,存在基类以为该特定类型提供服务。在这种情况下,可以使用多重继承来获取Union中所有类型数据的所有基类的服务。这也让我觉得继承的使用不当。但是,在这一点上是否有任何相同的概念来实现内容?
只是我的想法......
答案 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;
}
据我所知,这只适用于一些非常具体的元编程场景,或者可能是一些面向方面的编程。