我处理大量坐标数据,想想预定义的库类型
struct Point3d { double x,y,z; };
来自Eigen和OpenCV的等。
现在,每个点的坐标都以某个参照系表示。我希望类型系统能够跟踪表达每个点的帧。
的内容enum Frames { frame1, frame2 };
using Point_1 = TagWrapper<Point3d, frame1>;
using Point_2 = TagWrapper<Point3d, frame2>;
Point3d untagged_pt = ...;
Point_1 pt1 = ...;
Point_2 pt2 = ...;
Transform<frame1, frame2> tf_1_to_2 = ...; // from frame1 to frame2
// Compile time error, pt1 and pt2 are in different frames
auto pt3 = pt1 + pt2;
// Ok!, and typeof(pt4) == Point_2
auto pt4 = (tf_1_to_2 * pt1) + pt2;
// Compile time error, pt2 is not in frame1
auto pt5 = tf_1_to_2 * pt2;
// Ok!, and typeof(pt5) == Point_1
auto pt5 = untagged_pt + pt1;
最好我可以用任何&#34;标签包装任何类型&#34;使其成为标记类型。然后,所有类似标记的类型在彼此使用时表现为未标记类型,但是使用不同标记混合对象应该是编译时错误。我想在未标记和标记类型之间的操作结果被标记时也是有意义的。
这类似于单位,但我想将任何东西变成多种互斥的单位&#34;。因此,TagWrapper<Person, type1>
的界面为Person
,但不会与TagWrapper<Person, type2>
进行互动。
答案 0 :(得分:1)
要为不同的帧创建单独的类型,只需将其作为模板参数。然后我们需要在类型上定义我们想要的任何接口。以下是您可以编写的示例:
#include <utility> // for std::move
#include <iterator> // for std::begin, std::end
template <typename T, typename Tag, Tag kTag>
class TagWrapper
{
T value_;
public:
TagWrapper(T value)
: value_{ std::move(value) }
{}
// Note: This will allow you to add a T to a TagWrapper<T, ...>
// However, if T had an implicit constructor, you wouldn't be able
// to use that. If you wanted to support it, you'd have to 3x the operator overloads
// you implement. That is, you'd also need:
//
// friend auto operator+(T const& lhs, TagWrapper<T, Tag, kTag> const& rhs);
// friend auto operator+(TagWrapper<T, Tag, kTag> const& lhs, T const& rhs);
friend auto operator+(TagWrapper<T, Tag, kTag> const& lhs, TagWrapper<T, Tag, kTag> const& rhs)
{
return TagWrapper<T, Tag, kTag>{ lhs.value_ + rhs.value_ };
}
friend auto operator*(TagWrapper<T, Tag, kTag> const& lhs, TagWrapper<T, Tag, kTag> const& rhs)
{
return TagWrapper<T>{ lhs.value_ + rhs.value_ };
}
// the other arithmetic operators...
// You'd also want to do comparison operators
// Because it's impossible to completely delegate member functions on to
// everything that T can do, provide accessors to T. You may also prefer
// to define conversions, explicit or implicit:
//
// operator T const&() const { return value_; }
// explicit operator T const&() const { return value_; }
T const& get() const { return value_; }
T& get() { return value_; }
// As an example of generally wrapping, you could do this:
auto begin() { return std::begin(value_); }
auto begin() const { return std::begin(value_); }
auto end() { return std::end(value_); }
auto end() const { return std::end(value_); }
// This would make it so that if your type T was a "collection", then
// TagWrapper<T, ...> is as well. You could even use TagWrapper<T, ...>
// in a for-each loop
// Provide some way to see what the tag is. You may or may not want to expose this
static Tag tag = kTag;
};
如果您想在问题中使用所需的确切语法,可以将template <typename T, typename Tag, Tag kTag>
替换为template <typename T, Frames frame>
并进行必要的更改,或者您可以使用此类别别名:
template <typename T, Frames frame>
using MyTagWrapper = TagWrapper<T, Frames, frame>;
这样,混合两个标记类型将导致编译错误,但混合标记类型和未标记类型将转换为标记类型。剩下的就是定义标记类型之间的转换函数,这很容易做到:
MyTagWrapper<T, frame1> to_frame1(MyTagWrapper<T, frame2> const&);
然后这个:
auto pt4 = (tf_1_to_2 * pt1) + pt2;
成为这个:
auto pt4 = to_frame1(pt1) + pt2;