子向量和成员的 C++ 编译时解释
C++ compile time interpretation of sub-vectors and members
我有一个粒子class,它保持物体的位置、速度和加速度。我可以通过 .position()
、.velocity()
和 .acceleration()
访问向量的相关部分。我还可以访问每个数字,如 .velocity_x()
、.velocity_y()
、.velocity_z()
等。我想做的是通过这种方式访问速度的 z 部分:
p.velocity().z()
我想同时使用 .velocity()
(作为子向量)和 .velocity().z()
(作为单个数字)。
我也想避免定义任何额外的变量。我更喜欢在编译时解释所有内容(由于性能优先)。
这个实现可以用 c++ 实现吗?
在一次尝试中,我考虑过返回另一个带有仿函数的 class。但是除了编译时解释的问题之外,const
还存在一个问题,因为 .velocity()
不知道它后面是 const
还是非 const
形式的 .z()
.
#include <iostream>
#include <armadillo>
class Particle
{
public:
arma::vec::fixed<9> data;
inline double velocity_z() const
{
return data(5);
}
inline double& velocity_z()
{
return data(5);
}
inline const arma::subview_col<double> position() const
{
return data.subvec(0,2);
}
inline arma::subview_col<double> position()
{
return data.subvec(0,2);
}
inline const arma::subview_col<double> velocity() const
{
return data.subvec(3,5);
}
inline arma::subview_col<double> velocity()
{
return data.subvec(3,5);
}
inline const arma::subview_col<double> acceleration() const
{
return data.subvec(6,8);
}
inline arma::subview_col<double> acceleration()
{
return data.subvec(6,8);
}
};
arma::vec vector3(double x,double y,double z)
{
return {x,y,z};
}
int main()
{
Particle p;
p.position()=vector3(1.1,2.1,3.1);
p.velocity()=vector3(1.2,2.2,3.2);
p.velocity_z()=10.0;
p.acceleration()=vector3(1.3,2.3,3.3);
p.data.print();
return 0;
}
// output:
// 1.1000
// 2.1000
// 3.1000
// 1.2000
// 2.2000
// 10.0000
// 1.3000
// 2.3000
// 3.3000
制作:
g++ -std=c++11 test1.cpp -larmadillo
嗯,一个可能的解决方案是 return 一个代理对象,它既公开 .{x|y|z}()
成员函数又可以隐式转换为您的向量类型。
概念上是这样的:
#include <type_traits>
using vector_t = /* your library type */;
template<bool RValue>
class vector_proxy {
using return_type = typename std::conditional<RValue, vector_t&&, vector_t const&>::type;
vector_t &ref;
public:
vector_proxy(vector_t &ref) : ref(ref) {}
vector_proxy(vector_proxy const&) = delete;
vector_proxy& operator=(vector_proxy const&) = delete;
vector_proxy(vector_proxy&&) = delete;
vector_proxy& operator=(vector_proxy&&) = delete;
auto x() { /* code to produce x */ }
auto y() { /* code to produce y */ }
auto z() { /* code to produce z */ }
operator return_type() { return static_cast<return_type>(ref); }
};
为什么要模板?因为我想你会想要不同的行为,这取决于粒子对象的值类别。
如果粒子是左值,我们不希望 return 对粒子内部数据的非常量引用。如果它是一个右值,我们也可以 return 一个右值引用,所以代码的行为是 "as expected".
粒子的成员函数velocity()
可以有一个值类别限定符来区分这两种情况。上面的模板只是捕获了共同的行为并抽象了差异。
class particle {
// Members
public:
vector_proxy<false> velocity() const& { return {/* A ref to the velocity member */}; }
vector_proxy<true> velocity() && { return {/* A ref to the velocity member */}; }
// More functionality
};
自从您在评论中澄清 velocity
始终按值 return 设置一个新的矢量对象(顺便说一句,这是一种很好的默认方法),同时也允许修改粒子。上面的解决方案需要更新:
class particle;
template<bool ByRef>
class vector_proxy {
using return_type =
typename std::conditional<ByRef, double&, double>::type;
using ref_type =
typename std::conditional<ByRef, particle&, particle const&>::type;
ref_type ref;
public:
vector_proxy(ref_type ref) : ref(ref) {}
vector_proxy(vector_proxy const&) = delete;
vector_proxy& operator=(vector_proxy const&) = delete;
vector_proxy(vector_proxy&&) = delete;
vector_proxy& operator=(vector_proxy&&) = delete;
return_type x();
return_type y();
return_type z();
operator vector_t();
};
class particle {
// Members
template<bool>
friend class vector_proxy;
public:
vector_proxy<false> velocity() const { return {*this}; }
vector_proxy<true> velocity() { return {*this}; }
// More functionality
};
template<bool ByRef>
auto vector_proxy<ByRef>::x -> return_type {
return ref.data(3);
}
template<bool ByRef>
auto vector_proxy<ByRef>::y -> return_type {
return ref.data(4);
}
template<bool ByRef>
auto vector_proxy<ByRef>::z -> return_type {
return ref.data(5);
}
template<bool ByRef>
vector_proxy<ByRef>::operator vector_t() {
return ref.data.subvec(3, 5)
}
应该就是这样了。
我有一个粒子class,它保持物体的位置、速度和加速度。我可以通过 .position()
、.velocity()
和 .acceleration()
访问向量的相关部分。我还可以访问每个数字,如 .velocity_x()
、.velocity_y()
、.velocity_z()
等。我想做的是通过这种方式访问速度的 z 部分:
p.velocity().z()
我想同时使用 .velocity()
(作为子向量)和 .velocity().z()
(作为单个数字)。
我也想避免定义任何额外的变量。我更喜欢在编译时解释所有内容(由于性能优先)。
这个实现可以用 c++ 实现吗?
在一次尝试中,我考虑过返回另一个带有仿函数的 class。但是除了编译时解释的问题之外,const
还存在一个问题,因为 .velocity()
不知道它后面是 const
还是非 const
形式的 .z()
.
#include <iostream>
#include <armadillo>
class Particle
{
public:
arma::vec::fixed<9> data;
inline double velocity_z() const
{
return data(5);
}
inline double& velocity_z()
{
return data(5);
}
inline const arma::subview_col<double> position() const
{
return data.subvec(0,2);
}
inline arma::subview_col<double> position()
{
return data.subvec(0,2);
}
inline const arma::subview_col<double> velocity() const
{
return data.subvec(3,5);
}
inline arma::subview_col<double> velocity()
{
return data.subvec(3,5);
}
inline const arma::subview_col<double> acceleration() const
{
return data.subvec(6,8);
}
inline arma::subview_col<double> acceleration()
{
return data.subvec(6,8);
}
};
arma::vec vector3(double x,double y,double z)
{
return {x,y,z};
}
int main()
{
Particle p;
p.position()=vector3(1.1,2.1,3.1);
p.velocity()=vector3(1.2,2.2,3.2);
p.velocity_z()=10.0;
p.acceleration()=vector3(1.3,2.3,3.3);
p.data.print();
return 0;
}
// output:
// 1.1000
// 2.1000
// 3.1000
// 1.2000
// 2.2000
// 10.0000
// 1.3000
// 2.3000
// 3.3000
制作:
g++ -std=c++11 test1.cpp -larmadillo
嗯,一个可能的解决方案是 return 一个代理对象,它既公开 .{x|y|z}()
成员函数又可以隐式转换为您的向量类型。
概念上是这样的:
#include <type_traits>
using vector_t = /* your library type */;
template<bool RValue>
class vector_proxy {
using return_type = typename std::conditional<RValue, vector_t&&, vector_t const&>::type;
vector_t &ref;
public:
vector_proxy(vector_t &ref) : ref(ref) {}
vector_proxy(vector_proxy const&) = delete;
vector_proxy& operator=(vector_proxy const&) = delete;
vector_proxy(vector_proxy&&) = delete;
vector_proxy& operator=(vector_proxy&&) = delete;
auto x() { /* code to produce x */ }
auto y() { /* code to produce y */ }
auto z() { /* code to produce z */ }
operator return_type() { return static_cast<return_type>(ref); }
};
为什么要模板?因为我想你会想要不同的行为,这取决于粒子对象的值类别。
如果粒子是左值,我们不希望 return 对粒子内部数据的非常量引用。如果它是一个右值,我们也可以 return 一个右值引用,所以代码的行为是 "as expected".
粒子的成员函数velocity()
可以有一个值类别限定符来区分这两种情况。上面的模板只是捕获了共同的行为并抽象了差异。
class particle {
// Members
public:
vector_proxy<false> velocity() const& { return {/* A ref to the velocity member */}; }
vector_proxy<true> velocity() && { return {/* A ref to the velocity member */}; }
// More functionality
};
自从您在评论中澄清 velocity
始终按值 return 设置一个新的矢量对象(顺便说一句,这是一种很好的默认方法),同时也允许修改粒子。上面的解决方案需要更新:
class particle;
template<bool ByRef>
class vector_proxy {
using return_type =
typename std::conditional<ByRef, double&, double>::type;
using ref_type =
typename std::conditional<ByRef, particle&, particle const&>::type;
ref_type ref;
public:
vector_proxy(ref_type ref) : ref(ref) {}
vector_proxy(vector_proxy const&) = delete;
vector_proxy& operator=(vector_proxy const&) = delete;
vector_proxy(vector_proxy&&) = delete;
vector_proxy& operator=(vector_proxy&&) = delete;
return_type x();
return_type y();
return_type z();
operator vector_t();
};
class particle {
// Members
template<bool>
friend class vector_proxy;
public:
vector_proxy<false> velocity() const { return {*this}; }
vector_proxy<true> velocity() { return {*this}; }
// More functionality
};
template<bool ByRef>
auto vector_proxy<ByRef>::x -> return_type {
return ref.data(3);
}
template<bool ByRef>
auto vector_proxy<ByRef>::y -> return_type {
return ref.data(4);
}
template<bool ByRef>
auto vector_proxy<ByRef>::z -> return_type {
return ref.data(5);
}
template<bool ByRef>
vector_proxy<ByRef>::operator vector_t() {
return ref.data.subvec(3, 5)
}
应该就是这样了。