包装class的静态成员变量的一种简短方法
A short way to wrap class's static member variable
假设您有多个 类,并且它们都包含一个具有相同含义的静态变量,但它的名称在不同 类 中有所不同。
玩具示例:
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;
};
我想用 std::integral_constant
的 child 包装一个 "dimension" 变量。请注意,我无法编辑 'Point' 类,因为它们是某些外部库的一部分。这个实现对我有用,但看起来很笨拙(我使用的是 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> { };
有没有办法跳过所有这些 HasSomeVars
并像这样简短明了:
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> { };
此代码出现编译错误:
Error C2953: 'Dimension< T, unknown-type >': class template has already been defined
虽然这不是一个好方法,但是这对你有用:
#define DEFVAR(nm,val) static constexpr int nm = val;
class Point2D
{
public:
DEFVAR(dimension,2)
private:
double x, y;
};
class Point3D
{
public:
DEFVAR(dim,3)
private:
double x, y, z;
};
现在...完全不同的东西...
您可以定义几个 getDim()
模板 constexpr
、SFINAE enabled/disabled、函数。
具有 dim
的类型
template <typename T>
constexpr auto getDim () -> decltype( T::dim )
{ return T::dim; }
还有一个用于具有 dimension
的类型
template <typename T>
constexpr auto getDim () -> decltype( T::dimension )
{ return T::dimension; }
如果需要,您可以添加其他模板功能。
现在你的Dimension
模板class直接变成了
template <typename T>
struct Dimension
: std::integral_constant<decltype(getDim<T>()), getDim<T>()>
{ };
或者,如果你想给变量起一个不同的名字
template <typename T>
struct Dimension
{
static constexpr auto dim { getDim<T>() };
};
下面是一个完整的编译示例
#include <iostream>
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;
};
template <typename T>
constexpr auto getDim () -> decltype( T::dim )
{ return T::dim; }
template <typename T>
constexpr auto getDim () -> decltype( T::dimension )
{ return T::dimension; }
template <typename T>
struct Dimension
: std::integral_constant<decltype(getDim<T>()), getDim<T>()>
{ };
int main()
{
Dimension<Point2D> d2; // compile
Dimension<Point3D> d3; // compile
//Dimension<int> di; // compilation error
static_assert( Dimension<Point2D>::value == 2, "!" );
static_assert( Dimension<Point3D>::value == 3, "!" );
}
看来,虽然MSVC越来越好,但还是有一些表达SFINAE的情况它不能很好地处理。所以我们只需要帮助它一点点。与其尝试专门化相同的 class 模板或提供两个具有相同签名的不同函数,我们可以只提供两个 不同的 函数并重载它们:
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 {};
}
}
MSVC 似乎还不支持 template<auto>
,因此您只需重复该名称两次即可。这样,我们就可以为适当的结果添加别名:
template <typename T>
using Dimension = decltype(detail::get_dimensions<T>(0));
这为我编译了 godbolt 上的最新 MSVC(以及 gcc 和 clang)。
假设您有多个 类,并且它们都包含一个具有相同含义的静态变量,但它的名称在不同 类 中有所不同。
玩具示例:
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;
};
我想用 std::integral_constant
的 child 包装一个 "dimension" 变量。请注意,我无法编辑 'Point' 类,因为它们是某些外部库的一部分。这个实现对我有用,但看起来很笨拙(我使用的是 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> { };
有没有办法跳过所有这些 HasSomeVars
并像这样简短明了:
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> { };
此代码出现编译错误:
Error C2953: 'Dimension< T, unknown-type >': class template has already been defined
虽然这不是一个好方法,但是这对你有用:
#define DEFVAR(nm,val) static constexpr int nm = val;
class Point2D
{
public:
DEFVAR(dimension,2)
private:
double x, y;
};
class Point3D
{
public:
DEFVAR(dim,3)
private:
double x, y, z;
};
现在...完全不同的东西...
您可以定义几个 getDim()
模板 constexpr
、SFINAE enabled/disabled、函数。
具有 dim
template <typename T>
constexpr auto getDim () -> decltype( T::dim )
{ return T::dim; }
还有一个用于具有 dimension
template <typename T>
constexpr auto getDim () -> decltype( T::dimension )
{ return T::dimension; }
如果需要,您可以添加其他模板功能。
现在你的Dimension
模板class直接变成了
template <typename T>
struct Dimension
: std::integral_constant<decltype(getDim<T>()), getDim<T>()>
{ };
或者,如果你想给变量起一个不同的名字
template <typename T>
struct Dimension
{
static constexpr auto dim { getDim<T>() };
};
下面是一个完整的编译示例
#include <iostream>
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;
};
template <typename T>
constexpr auto getDim () -> decltype( T::dim )
{ return T::dim; }
template <typename T>
constexpr auto getDim () -> decltype( T::dimension )
{ return T::dimension; }
template <typename T>
struct Dimension
: std::integral_constant<decltype(getDim<T>()), getDim<T>()>
{ };
int main()
{
Dimension<Point2D> d2; // compile
Dimension<Point3D> d3; // compile
//Dimension<int> di; // compilation error
static_assert( Dimension<Point2D>::value == 2, "!" );
static_assert( Dimension<Point3D>::value == 3, "!" );
}
看来,虽然MSVC越来越好,但还是有一些表达SFINAE的情况它不能很好地处理。所以我们只需要帮助它一点点。与其尝试专门化相同的 class 模板或提供两个具有相同签名的不同函数,我们可以只提供两个 不同的 函数并重载它们:
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 {};
}
}
MSVC 似乎还不支持 template<auto>
,因此您只需重复该名称两次即可。这样,我们就可以为适当的结果添加别名:
template <typename T>
using Dimension = decltype(detail::get_dimensions<T>(0));
这为我编译了 godbolt 上的最新 MSVC(以及 gcc 和 clang)。