包装类的静态成员变量的简短方法

时间:2018-04-27 13:13:29

标签: c++ templates c++14 template-meta-programming sfinae

想象一下,你有几个类,它们都包含一个具有相同含义的静态变量,但它的名称在不同的类中有所不同。

玩具示例:

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的孩子包装一个“维度”变量。请注意,我无法编辑'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> { };

此代码出现编译错误:

  

错误C2953:'尺寸&lt; T,未知类型&gt;':类模板已经定义

3 个答案:

答案 0 :(得分:2)

似乎虽然MSVC越来越好,但仍然有一些表达SFINAE的案例,它无法应对。所以我们只需要帮助它一点点。我们可以只提供两个不同的函数并重载它们,而不是尝试专门化同一个类模板或提供两个不同的函数,而不是:

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(以及gcc和clang)的最新MSVC。

答案 1 :(得分:1)

现在......对于完全不同的事情......

您可以定义几个getDim()模板constexpr,启用/禁用SFINAE,功能。

一个用于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模板类直接成为

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, "!" );
 }

答案 2 :(得分:-1)

虽然这不是一个好方法,但这对你有用:

if randint == var: