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

She*_*ope 10 c++ templates sfinae template-meta-programming c++14

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

玩具示例:

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;
};
Run Code Online (Sandbox Code Playgroud)

我想用一个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> { };
Run Code Online (Sandbox Code Playgroud)

有没有办法跳过所有这些,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> { };
Run Code Online (Sandbox Code Playgroud)

此代码出现编译错误:

错误C2953:'Dimension <T,unknown-type>':已经定义了类模板

Bar*_*rry 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 {};
    }
}
Run Code Online (Sandbox Code Playgroud)

MSVC 似乎还不支持template<auto>,所以你只需重复该名称两次即可。这样,我们就可以为适当的结果添加别名:

template <typename T>
using Dimension = decltype(detail::get_dimensions<T>(0));
Run Code Online (Sandbox Code Playgroud)

它使用 godbolt 上最新的 MSVC(以及 gcc 和 clang)为我进行编译。