更好的转换功能

Better conversion functions

本文关键字:功能 转换 更好      更新时间:2023-10-16

我在 C++0x 框架中有各种类,并希望编写函数以在其中一些类之间进行转换。例如:

struct Foo { float v; };
struct Bar { int i; };
struct Tar { float p, q; };
void Convert(const Foo& x, Bar& y) { y.i = static_cast<int>(x.v); }
void Convert(const Tar& x, Foo& y) { y.v = x.p + x.q; }

这只是一个例子。有很多"小"类。并非所有转换函数都有意义。

此外,还有一些类本质上表现得像 STL 容器,应该"继承"这些转换函数。

void Convert(const std::vector<Foo>& cx, std::vector<Bar>& cy) { ... }
void Convert(const std::vector<Tar>& cx, std::vector<Bar>& cy) { ... }

不,我正在寻找一种简单的方法来定义这些函数。我试过了:

template<typename X, typename Y>
void Convert(const std::vector<X>& cx, std::vector<Y>& cy) {
   cy.resize(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     Convert(cx[i], cy[i]);
   }
}

这非常有效。

但是,通过这样的设置,必须编写

std::vector<X> cx = { ... };
std::vector<Y> cy;
Convert(cx, cy);
// when not specifying the type, one needs to use this form f(X, &Y)

使用这样的设置

template<typename X, typename Y>
std::vector<Y> Convert(const std::vector<X>& cx) {
   std::vector<Y> cy(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     cy[i] = Convert(cx[i]);
   }
   return cy;
}

一个人必须写

std::vector<X> cx = { ... };
std::vector<Y> cy = Convert<X,Y>(cx);
// can I avoid specifying the source type with this form?

当然,在某些时候需要提及目标类型,但源类型由函数参数定义。我不想一遍又一遍地提及它。

有没有一种优雅的通用方法来处理这样的转换函数?

编辑了问题以进行澄清

为什么不简单地使用构造函数来实现此目的呢?

explicit Bar(const Foo& x) : i(static_cast<int>(x.v)) {}

您是否考虑过使用 ctors:

struct Bar {
   int i;
   explicit Bar(Foo const &f) : i(static_cast<int>(f.v)) {}
};

然后你可以做:

Bar x = static_cast<Bar>(some_foo);

您可能真的要明确表示。如果消除它(允许隐式转换(,则转换向量变得微不足道:

std::vector<foo> foos;
// code to populate foos ...
std::vector<Bar> bars((foos.begin()), foos.end());

至于是否明确表示,问题是您是否可能会意外地从Foo转换为Bar,从而发生您真正不希望发生的事情。这是一个非常普遍的问题,比如转换为bool,但从一个用户定义类型转换到另一个用户定义类型的问题要少得多。在这种情况下,关闭explicit可能很方便且合理安全。

由于返回值优化,您不需要使用 out 参数,只需使用返回值:

void Convert(const X& x, Y& y);

成为:

Y Convert(const X& x);

所以现在你可以在表达式中使用它。

还可以考虑改用转换构造函数转换运算符

Y::Y(const X&);
X::operator Y();

这些将允许类型之间的隐式转换(如果这是您想要的 - 否则您可以显式声明它们(。

要处理 STL 容器,请使用迭代器范围:

template<class FwdIterator>
Y Convert(FwdIterator begin, FwdIterator end);

在这种样式中,它不仅与所有 STL 容器兼容,还将与原始指针范围兼容。

另请注意,如果声明隐式转换构造函数或转换运算符,则可以使用 std::copy:

vector<X> xs = ...;
vector<Y> ys;
copy(xs.begin(), xs.end(), back_inserter(ys.begin()));

这将在将元素从 xs 转换为 ys 时隐式调用用户定义的转换。

实际上,我找到了如何做到这一点。通过对模板参数重新排序

template<typename Y, typename X>
std::vector<Y> ConvertTo(const std::vector<X>& cx) {
   std::vector<Y> cy(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     cy[i] = Convert(cx[i]);
   }
   return cy;
}

一个人可以写

std::vector<Y> cy = ConvertTo<Y>(cx);

编译器会自动推断输入的第二种模板类型。不错的功能!