在非静态数据成员上使用 SFINAE?
Using SFINAE on non static data members?
我是 SFINAE 的新手,我正在尝试编写一个简单的 Vector 模板。我想要实现的是根据为 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 向量使用了专门的构造函数(请参阅上面的代码)。
但 intellisense 仍然报告 Members 'x', 'y', 'z' are not initialized in this constructor.
如有任何帮助,我们将不胜感激。
如果需要,请将条目 class 专门化为 1、2、3 维,即 template<> class Vector<3> { ... }
。
这里你真的不需要SFINAE。只需使用专业化:
template<unsigned int DIMENSIONS>
class Vector {
template<int I>
struct ZContainer {};
template<> struct ZContainer<3> {
float z;
};
ZContainer<DIMENSIONS> possibleZ;
};
这样做的好处是您不需要额外的模板。您可以只为您的 class 行为向 ZContainers 添加函数。
您根本无法有条件地启用这样的变量。您最好的选择就是提供 Vector
:
的多个专业化
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]...}}
{ }
};
你可以这样做:
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'
}
不过,这样好吗solution/design?我不确定;它会变得混乱。它很可能会在实施过程中出现其他困难...
您可以使用继承。
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;
};
然后定义你的向量class:
template<int n>
struct Vector : VecBase<n> {
// operations
};
如果要进行n维运算,可以使用std::get
和std::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 实现它们。
然后,您可以通过创建一个实现操作的基础 class 来实现您的操作:
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>> {};
请注意,我目前没有可用的编译器。如果您遇到编译器错误,请发表评论,我会检查一下。
我是 SFINAE 的新手,我正在尝试编写一个简单的 Vector 模板。我想要实现的是根据为 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 向量使用了专门的构造函数(请参阅上面的代码)。
但 intellisense 仍然报告 Members 'x', 'y', 'z' are not initialized in this constructor.
如有任何帮助,我们将不胜感激。
如果需要,请将条目 class 专门化为 1、2、3 维,即 template<> class Vector<3> { ... }
。
这里你真的不需要SFINAE。只需使用专业化:
template<unsigned int DIMENSIONS>
class Vector {
template<int I>
struct ZContainer {};
template<> struct ZContainer<3> {
float z;
};
ZContainer<DIMENSIONS> possibleZ;
};
这样做的好处是您不需要额外的模板。您可以只为您的 class 行为向 ZContainers 添加函数。
您根本无法有条件地启用这样的变量。您最好的选择就是提供 Vector
:
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]...}}
{ }
};
你可以这样做:
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'
}
不过,这样好吗solution/design?我不确定;它会变得混乱。它很可能会在实施过程中出现其他困难...
您可以使用继承。
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;
};
然后定义你的向量class:
template<int n>
struct Vector : VecBase<n> {
// operations
};
如果要进行n维运算,可以使用std::get
和std::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 实现它们。
然后,您可以通过创建一个实现操作的基础 class 来实现您的操作:
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>> {};
请注意,我目前没有可用的编译器。如果您遇到编译器错误,请发表评论,我会检查一下。