I'm stuck with c++17 for a project, so I don't have access to designated initializers. I have a bunch of union types that I want to avoid initializing this way (because it is annoying):
MyUnionType x;
x.value = value_set_for_all_union_members;
I want to instead have this
MyUnionType x(value_set_for_all_union_members);
But I also want to avoid writing an implementation for each union I create. I know that all my union types are going to be of the following structure, each union is meant to actually represent bit a bit field, so I actually do want type pruning here, I know it is "UB" according to C++, but that is on the C++ committee, in C it is not undefined behavior, and thus all compilers that I care about will do what I want here.
union Example{
integer_type value;
custom_safe_bitfield_abstraction<...> a;
custom_safe_bitfield_abstraction<...> b;
...
};
I thought, okay, I'll just inherit the constructor, and use CRTP to extract the appropriate integer_type
. Of course I can't inherit on a union directly, so instead I opted for this strategy:
struct Example : Base<Example>{
union{
integer_type value;
custom_safe_bitfield_abstraction<...> a;
custom_safe_bitfield_abstraction<...> b;
...
};
};
using an anonymous union, I should be able to use it the same exact way as before (example.value should be the value inside of union).
Then in the implementation I do the following:
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;
}
}
This however doesn't work:
error: Incomplete type 'Example' used in nested name specifier
> using value_type = decltype(Derived_T::value);
Apparently we aren't allowed to refer to a member before it has been declared. Okay... but there must be some way to extract the type data out, after all I don't care about any memory alignment or anything.
The only other thing I can think of, is include the type in the CRTP template parameter (ie Base<Derived_T, value_type>
) but I want to avoid doing that. I imagine there is some method for writing a function or specifying an internal type on each derived class, I don't want to do that either (and sort of defeats the purpose of what I'm doing anyway).
Is there a way to avoid writing the constructor per class, and with out sacrificing the other code duplication minimization goals I have?
Not exactly what you asked... but you can use the fact that you can use the type of D::value
inside a member function... so using SFINAE over a template contructor...
I mean, you can write something as
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; }
};
where the template constructor is enabled only if the deduced type of the argument is of the same type of B::value
.
Remember also to add the using
using Base<Example>::Base;
inside Example
.
The following is a full compiling 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
}