"this object looks like a 3D vector" 的概念
Concept for "this object looks like a 3D vector"
我有一个项目使用了一些库,其中每个库都定义了某种 3D 向量,例如我在其他库上使用 SFML's 3D vector in some parts of the code, reactphysics3d's Vector3
,还有另一个 3D来自另一个库的向量。
现在我需要为每个向量编写叉积和 std::ostream &operator <<
代码:
constexpr sf::Vector3f cross(const sf::Vector3f &a, const sf::Vector3f &b)
{
return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
std::ostream &operator <<(std::ostream &o, const sf::Vector3f &v)
{
return o << '{' << v.x << ", " << v.y << ", " << v.z << '}';
}
// ... repeat for every 3D vector type
这意味着很多代码重复,所以我改变了方法:
template <typename vector3a_t, typename vector3b_t>
constexpr vector3a_t cross(const vector3a_t &a, const vector3b_t &b)
{
return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
现在 cross
函数有两种类型,它们预期具有成员 x
、y
和 z
,但显然这不适用于 std::ostream &operator <<
:
template <typename vector3_t>
std::ostream &operator <<(std::ostream &o, const vector3_t &v)
{
return o << '{' << v.x << ", " << v.y << ", " << v.z << '}';
}
因为模板类型 vector3_t
遮蔽了所有内容,所以我想知道是否可以限制函数接受任何符合“ 应该看起来像 3D 矢量的概念的类型":
template <typename vector_t>
concept vector3_c = requires(vector_t v) { // error: expected unqualified-id
{ std::is_scalar_v<decltype(v.x)> } -> true;
{ std::is_scalar_v<decltype(v.y)> } -> true;
{ std::is_scalar_v<decltype(v.z)> } -> true;
};
template <vector3_c A, vector3_c B>
constexpr decltype(A) cross(const A &a, const B &b)
{
return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
template <vector3_c V>
std::ostream &operator <<(std::ostream &o, const V &v)
{
return o << '{' << v.x << ", " << v.y << ", " << v.z << '}';
}
但这甚至无法编译。这是我第一次尝试概念,我不知道我正在尝试做的事情是否可行。
这个
template <typename vector_t>
concept vector3_c = requires(vector_t v) { // error: expected unqualified-id
{ std::is_scalar_v<decltype(v.x)> } -> true;
};
只会检查表达式 std::is_scalar_v<decltype(v.x)>
的 有效性 。另外,return-type-requirement约束的是类型而不是值,所以-> true
不正确,应该是->std::same_as<bool>
.
您应该使用嵌套 requires
来 计算 表达式的值,例如
template<typename vector_t>
concept vector3_c = requires(vector_t v) {
requires std::is_scalar_v<decltype(v.x)> &&
std::is_scalar_v<decltype(v.y)> &&
std::is_scalar_v<decltype(v.z)>;
};
如果只是想检测一个struct是否有有效的成员变量,使用{v.x} -> scalar
形式的约束表达式更直观
template<typename T>
concept scalar = std::is_scalar_v<T>;
template<typename vector_t>
concept vector3_c = requires(vector_t v) {
{ auto(v.x) } -> scalar;
{ auto(v.y) } -> scalar;
{ auto(v.z) } -> scalar;
};
其中auto(x)
是C++23支持decay-copy的语言,我们可以利用它来去除成员变量访问的引用。
如果您想要检查一个类型是否具有作为标量的 x、y、z 成员,您可以这样做:
//Ensures that the user won't do something weird
template<typename T>
concept ref_to_scalar = std::is_reference_v<T> && std::is_scalar_v<std::remove_reference_t<T>>;
template<typename Vec>
concept vector_2d = requires(Vec v) {
{v.x} -> ref_to_scalar;
{v.y} -> ref_to_scalar;
};
//vec3d subsumes vec2d
template<typename Vec>
concept vector_3d = vector_2d<Vec> && requires(Vec v) {
{v.z} -> ref_to_scalar;
};
我有一个项目使用了一些库,其中每个库都定义了某种 3D 向量,例如我在其他库上使用 SFML's 3D vector in some parts of the code, reactphysics3d's Vector3
,还有另一个 3D来自另一个库的向量。
现在我需要为每个向量编写叉积和 std::ostream &operator <<
代码:
constexpr sf::Vector3f cross(const sf::Vector3f &a, const sf::Vector3f &b)
{
return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
std::ostream &operator <<(std::ostream &o, const sf::Vector3f &v)
{
return o << '{' << v.x << ", " << v.y << ", " << v.z << '}';
}
// ... repeat for every 3D vector type
这意味着很多代码重复,所以我改变了方法:
template <typename vector3a_t, typename vector3b_t>
constexpr vector3a_t cross(const vector3a_t &a, const vector3b_t &b)
{
return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
现在 cross
函数有两种类型,它们预期具有成员 x
、y
和 z
,但显然这不适用于 std::ostream &operator <<
:
template <typename vector3_t>
std::ostream &operator <<(std::ostream &o, const vector3_t &v)
{
return o << '{' << v.x << ", " << v.y << ", " << v.z << '}';
}
因为模板类型 vector3_t
遮蔽了所有内容,所以我想知道是否可以限制函数接受任何符合“ 应该看起来像 3D 矢量的概念的类型":
template <typename vector_t>
concept vector3_c = requires(vector_t v) { // error: expected unqualified-id
{ std::is_scalar_v<decltype(v.x)> } -> true;
{ std::is_scalar_v<decltype(v.y)> } -> true;
{ std::is_scalar_v<decltype(v.z)> } -> true;
};
template <vector3_c A, vector3_c B>
constexpr decltype(A) cross(const A &a, const B &b)
{
return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
template <vector3_c V>
std::ostream &operator <<(std::ostream &o, const V &v)
{
return o << '{' << v.x << ", " << v.y << ", " << v.z << '}';
}
但这甚至无法编译。这是我第一次尝试概念,我不知道我正在尝试做的事情是否可行。
这个
template <typename vector_t>
concept vector3_c = requires(vector_t v) { // error: expected unqualified-id
{ std::is_scalar_v<decltype(v.x)> } -> true;
};
只会检查表达式 std::is_scalar_v<decltype(v.x)>
的 有效性 。另外,return-type-requirement约束的是类型而不是值,所以-> true
不正确,应该是->std::same_as<bool>
.
您应该使用嵌套 requires
来 计算 表达式的值,例如
template<typename vector_t>
concept vector3_c = requires(vector_t v) {
requires std::is_scalar_v<decltype(v.x)> &&
std::is_scalar_v<decltype(v.y)> &&
std::is_scalar_v<decltype(v.z)>;
};
如果只是想检测一个struct是否有有效的成员变量,使用{v.x} -> scalar
template<typename T>
concept scalar = std::is_scalar_v<T>;
template<typename vector_t>
concept vector3_c = requires(vector_t v) {
{ auto(v.x) } -> scalar;
{ auto(v.y) } -> scalar;
{ auto(v.z) } -> scalar;
};
其中auto(x)
是C++23支持decay-copy的语言,我们可以利用它来去除成员变量访问的引用。
如果您想要检查一个类型是否具有作为标量的 x、y、z 成员,您可以这样做:
//Ensures that the user won't do something weird
template<typename T>
concept ref_to_scalar = std::is_reference_v<T> && std::is_scalar_v<std::remove_reference_t<T>>;
template<typename Vec>
concept vector_2d = requires(Vec v) {
{v.x} -> ref_to_scalar;
{v.y} -> ref_to_scalar;
};
//vec3d subsumes vec2d
template<typename Vec>
concept vector_3d = vector_2d<Vec> && requires(Vec v) {
{v.z} -> ref_to_scalar;
};