Answer of your question
if
statements are not marked constexpr
The reason why your code does not compile is because, even if the if
statements will be evaluated to false at compile time, the branches contained by those if
statements will be compiled.
In your example, you can mark those if
statements as constexpr
to compile the code.
However, it will (probably) not work as you intend it to, because the type of property
will be an rvalue of the pointer you passed to the constructor.
Here is an example of how I made it work (if I understood correctly what you were trying to do:
struct PropertyPointer {
int type;
std::string name;
union {
std::reference_wrapper<int*> int_reference;
std::reference_wrapper<double*> double_reference;
std::reference_wrapper<float*> float_reference;
std::reference_wrapper<std::string*> string_reference;
};
PropertyPointer(std::string name, auto&& property): name{name} {
using Type = ::std::remove_cvref_t<decltype(property)>;
if constexpr (std::is_same<Type, int*>::value) {
type = 'i';
int_reference = property;
} else if constexpr (std::is_same<Type, double*>::value) {
type = 'd';
double_reference = property;
} else if constexpr (std::is_same<Type, float*>::value) {
type = 'f';
float_reference = property;
} else if constexpr (std::is_same<Type, std::string*>::value) {
type = 's';
string_reference = property;
}
}
};
std::variant
It seems to me that you are not familiar with std::variant
.
The class template std::variant represents a type-safe union.
source
Its job is to replace unions (that is a C feature) with type safe classes with extra functionalities.
Here is a quick example of how I would have done it:
class PropertyPointer {
public:
PropertyPointer(
auto&& property
)
: m_value{ &property }
{
// compare types if you want it to compile but have the object empty
}
template <
typename T
> [[ nodiscard ]] bool hasType() // true if the types matches the one inside (pointer or not)
{
using Type = ::std::remove_cvref_t<::std::remove_pointer_t<::std::remove_cvref_t<T>>>;
return std::holds_alternative<Type*>(m_value);
}
private:
::std::variant<int*, float*, double*, ::std::string*> m_value;
};
int main()
{
PropertyPointer property{ ::std::string{ "yes" } };
property.hasType<int>(); // false
property.hasType<float>(); // false
property.hasType<double>(); // false
property.hasType<::std::string>(); // true
property.hasType<const ::std::string>(); // true
property.hasType<::std::string&>(); // true
property.hasType<const ::std::string*>(); // true
property.hasType<const ::std::string *const>(); // true
}
This example is here to show you that you can add some extra features like comparing with and without the pointer for the same behavior.
This example isn't here to tell you that "it is how it has to be done" but rather an example of how modern c++ features can make your life easier.