在非静态数据成员上使用SFINAE?

时间:2016-07-07 14:13:21

标签: c++ template-specialization sfinae

我是SFINAE的新手,我正在尝试编写一个简单的矢量模板。我想要实现的是根据为Vector设置的维度启用z成员变量。

我尝试使用以下代码实现此效果:

template<unsigned int DIMENSIONS>
class Vector {
    // The x and y variables (same as z)

    template<typename = typename std::enable_if<DIMENSIONS >= 3>::type>
    /// <summary>
    /// The z coordinate.
    /// </summary>
    Float& z;

    Vector() : x(values[0]), y(values[1]) {
    }

    // Other member functions and variables

    std::vector<float> values;
};

template <>
Vector<3>::Vector() : x(values[0]), y(values[1]), z(values[2]) {
}

当有3个或更多维度时,应该启用z变量。这使编译器抱怨以下错误:

'Vector<DIMENSIONS>::z': only static data member templates are allowed

另外,在这种情况下,我不完全确定如何在SFINAE中使用初始化列表。因为如果维度小于3,则不必初始化z(因为它不存在)。到目前为止,我已经为3D向量使用了专门的构造函数(参见上面的代码)。

但是,Intelliisense仍然报告Members 'x', 'y', 'z' are not initialized in this constructor.

任何帮助都将不胜感激。

5 个答案:

答案 0 :(得分:5)

你可以这样做:

struct no_z_var {};

struct z_var
{
    float z;
};

template<std::size_t n>
struct my_vector : std::conditional_t<( n >= 3 ), z_var, no_z_var>
{
    float x, y;
};

int main()
{
    my_vector<3> mv3;
    mv3.z = 3.4f;
    my_vector<2> mv2;
    mv2.z = 3.4f; // error; no 'z'
}

然而,这是一个很好的解决方案/设计吗?我不太确定;它会变得混乱。在实施过程中很可能会出现其他困难......

答案 1 :(得分:2)

你根本无法有条件地启用这样的变量。您最好的选择就是提供template <> struct Vector<2> { float x, y; Vector(std::vector<float> const& values) : x(values[0]) , y(values[1]) { } }; template <> struct Vector<3> { float x, y, z; Vector(std::vector<float> const& values) : x(values[0]) , y(values[1]) , z(values[2]) { } }; // etc. 的多个专业化:

template <size_t DIM>
struct Vector {
    std::array<float, DIM> values;

    Vector(std::vector<float> const& vs)
    : Vector(vs, std::make_index_sequence<DIM>{})
    { }

private:
    template <size_t... Is>
    Vector(std::vector<float> const& vs, std::index_sequence<Is...> )
    : values{{vs[Is]...}}
    { }
};

虽然使用数组可能更直接:

font-family

答案 2 :(得分:1)

您可以使用继承。

template<int n>
struct VecBase;

template<>
struct VecBase<1> {
    float x;
};

template<>
struct VecBase<2> : VecBase<1> {
    float y;
};

template<>
struct VecBase<3> : VecBase<2> {
    float z;
};

template<>
struct VecBase<4> : VecBase<3> {
    float w;
};

然后定义你的矢量类:

template<int n>
struct Vector : VecBase<n> {
    // operations
};

如果您想进行n维运算,可以使用std::getstd::index_sequence。让我们首先为std::get

进行重载
namespace std {

template<size_t I, int n, enable_if_t<(I == 0 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
    return vec.x;
}

template<size_t I, int n, enable_if_t<(I == 1 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
    return vec.y;
}

template<size_t I, int n, enable_if_t<(I == 2 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
    return vec.z;
}

template<size_t I, int n, enable_if_t<(I == 3 && I < n), int> = 0>
float& get(VecBase<n>& vec) {
    return vec.w;
}

}

请注意,您必须为const实现它们。

然后,您可以通过创建实现操作的基类来实现您的操作:

template<int, typename>
struct VecOps;

template<int n, std::size_t... S>
struct VecOps<n, std::index_sequence<S...>> : VecBase<n> {
    float dotp(Vector<n>& vec) const {
        // Where sum is adding each parameter variadically.
        return sum((std::get<S>(*this) * std::get<S>(vec))...);
    }
};

最后,让Vector延长VecOps

template<int n>
struct Vector : VecOps<n, std::make_index_sequence<n>> {};

请注意,目前我的处置中没有编译器。如果您遇到编译器错误,只需发表评论即可查看。

答案 3 :(得分:0)

如果需要,请将entrie类专门设置为1,2,3维,即template<> class Vector<3> { ... }

答案 4 :(得分:0)

你真的不需要SFINAE。只需使用专业化:

template<unsigned int DIMENSIONS>
class Vector {
  template<int I>
  struct ZContainer {};
  template<> struct ZContainer<3> {
    float z;
  };
  ZContainer<DIMENSIONS> possibleZ;
};

这样做的好处是您不需要其他模板。您只需向ZContainers添加函数以用于您的类行为。