避免在 C++17 中为每个联合样式 class 编写构造函数

Avoid writing constructor every union style class in C++17

我在一个项目中坚持使用 c++17,所以我无法访问指定的初始化程序。我有一堆联合类型,我想避免以这种方式初始化(​​因为它很烦人):

MyUnionType x;
x.value = value_set_for_all_union_members;

我想改用这个

MyUnionType x(value_set_for_all_union_members); 

但我也想避免为我创建的每个联合编写实现。我知道我所有的联合类型都将具有以下结构,每个联合实际上代表一个位域,所以我确实想要在这里进行类型修剪,我知道根据 C++,它是“UB”,但是在 C++ 委员会中,在 C 中它不是未定义的行为,因此我关心的所有编译器都会在这里做我想做的事。

union Example{
    integer_type value;
    custom_safe_bitfield_abstraction<...> a;
    custom_safe_bitfield_abstraction<...> b;
    ...
};

我想,好吧,我就继承构造函数,并使用CRTP提取适当的integer_type。当然我不能直接继承联合,所以我选择了这个策略:

struct Example : Base<Example>{
    union{
        integer_type value;
        custom_safe_bitfield_abstraction<...> a;
        custom_safe_bitfield_abstraction<...> b;
        ...
    };
};

使用匿名联合,我应该能够像以前一样使用它(example.value 应该是联合内部的值)。

然后在实现中我做了以下事情:

template<class Derived_T>
struct Base{
    using value_type = decltype(Derived_T::value); 
    explicit Base(value_type v){
        static_cast<Derived_T*>(this)->value = v; 
    }
}

但这不起作用:

error: Incomplete type 'Example' used in nested name specifier
>    using value_type = decltype(Derived_T::value); 

显然我们不允许在成员声明之前引用它。好的...但是必须有一些方法来提取类型数据,毕竟我不关心任何内存对齐或任何东西。

我唯一能想到的另一件事是在 CRTP 模板参数中包含类型(即 Base<Derived_T, value_type>),但我想避免这样做。我想有一些方法可以编写一个函数或在每个派生 class 上指定一个内部类型,我也不想这样做(并且有点违背我正在做的事情的目的)。

有没有一种方法可以避免按照 class 编写构造函数,并且不会牺牲我的其他代码重复最小化目标?

不完全是你问的...但是你可以使用这样一个事实,即你可以在成员函数中使用 D::value 的类型...所以在模板构造函数上使用 SFINAE...

我的意思是,你可以这样写

template <typename D>
struct Base
 {
   template <typename T>
   static constexpr bool is_value_type ()
    { return std::is_same_v<decltype(D::value), T>; }

   template <typename T, bool B = is_value_type<T>(),
             std::enable_if_t<B, int> = 0>
   explicit Base (T v)
    { static_cast<D*>(this)->value = v; }
 };

仅当推导的参数类型与 B::value.

类型相同时才启用模板构造函数

记得还要添加 using

using Base<Example>::Base;

里面 Example.

下面是一个完整的编译示例

#include <type_traits>

template <typename D>
struct Base
 {
   template <typename T>
   static constexpr bool is_value_type ()
    { return std::is_same_v<decltype(D::value), T>; }

   template <typename T, bool B = is_value_type<T>(),
             std::enable_if_t<B, int> = 0>
   explicit Base (T v)
    { static_cast<D*>(this)->value = v; }
 };

struct Example : Base<Example>
 {
   using Base<Example>::Base;

   union
    {
      long value;
      long a;
      long b;
    };
 };

int main ()
 {
   //Example  e0{0};   // compilation error
   Example  e1{1l};    // compile
   //Example  e2{2ll}; // compilation error
 }