Imagine that you have several classes and all of them contain a static variable of the same meaning, but it's names differ in different classes.
A toy example:
class Point2D
{
public:
static constexpr int dimension = 2;
private:
double x, y;
}
class Point3D
{
public:
static constexpr int dim = 3;
private:
double x, y, z;
};
I want to wrap a "dimension" variable with a std::integral_constant
's child. Note that I can't edit 'Point' classes, because they are parts of some external libraries. This implementation works for me, but looks clumsy (I'm using VS2017):
template <typename T, typename = void>
struct HasDimensionVar : std::false_type { };
template <typename T>
struct HasDimensionVar<T, decltype( T::dimension, void( ) )> : std::true_type { };
template <typename T, typename = void>
struct HasDimVar : std::false_type { };
template <typename T>
struct HasDimVar<T, decltype( T::dim, void( ) )> : std::true_type { };
template <typename T, class Enable = void>
struct Dimension;
template <typename T>
struct Dimension<T, std::enable_if_t< HasDimensionVar<T>::value> > :
std::integral_constant<decltype( T::dimension ), T::dimension> { };
template <typename T>
struct Dimension<T, std::enable_if_t< HasDimVar<T>::value> > :
std::integral_constant<decltype( T::dim ), T::dim> { };
Is there a way to skip all these HasSomeVars
and have something short and clear like this:
template <typename T, class Enable = void>
struct Dimension;
template <typename T>
struct Dimension<T, decltype( T::dimension, void( ) ) > :
std::integral_constant<decltype( T::dimension ), T::dimension> { };
template <typename T>
struct Dimension<T, decltype( T::dim, void( ) ) > :
std::integral_constant<decltype( T::dim ), T::dim> { };
This code gets a compile error:
Error C2953: 'Dimension< T, unknown-type >': class template has already been defined
Static variables can be accessed by calling with the class name ClassName. VariableName. When declaring class variables as public static final, then variable names (constants) are all in upper case. If the static variables are not public and final, the naming syntax is the same as instance and local variables.
We can define class members static using static keyword. When we declare a member of a class as static it means no matter how many objects of the class are created, there is only one copy of the static member. A static member is shared by all objects of the class.
Static Member Functions in C++ To create a static member function we need to use the static keyword while declaring the function. Since static member variables are class properties and not object properties, to access them we need to use the class name instead of the object name.
We can put static members (Functions or Variables) in C++ classes. For the static variables, we have to initialize them after defining the class. To initialize we have to use the class name then scope resolution operator (::), then the variable name. Now we can assign some value.
It seems that while MSVC is getting better and better, there are still a few cases of expression SFINAE that it can't quite deal with with. So we just gotta help it out a little bit. Instead of trying to specialize the same class template or provide two different functions with the same signature, we can just provide two different functions and overload on them:
namespace detail {
template <typename T>
constexpr std::integral_constant<decltype(T::dim), T::dim>
get_dimensions(int)
{
return {};
}
template <typename T>
constexpr std::integral_constant<decltype(T::dimension), T::dimension>
get_dimensions(long)
{
return {};
}
}
It doesn't seem like MSVC supports template<auto>
yet, so you'll just have to repeat the name twice. With that, we can just alias the appropriate result:
template <typename T>
using Dimension = decltype(detail::get_dimensions<T>(0));
This compiles for me with the latest MSVC on godbolt (as well as gcc and clang).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With