避免在 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
}
我在一个项目中坚持使用 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
}