我觉得这个问题之前一定是问过,但我找不到谷歌上的答案。如果有,请指示我链接,我将删除此帖。
考虑这个代表我遇到的更大问题的最小例子。假设我创建了一个简单的“Point”和“Printer”类,如下所示:
class Point {
public:
double x, y;
Point() {x = y = 0;}
Point(double x, double y) {
this->x = x; this->y = y;
}
};
template<typename T>
class Printer {
public:
T* mData;
int mSize;
// Constructor
Printer(std::vector<T> &input) {
mData = &input[0];
mSize = input.size();
}
// Simple Print function
void Print() {
printf(" - Showing %d items\n", mSize);
for (int i = 0; i < mSize; i++) {
const T &item = mData[i];
printf(" - Item %d: (%lf, %lf)\n", i, item.x, item.y);
}
}
};
我可以像这样使用打印机类:
std::vector<Point> points; // fill the vector, and then...
Printer<Point> pointsPrinter(points); pointsPrinter.Print();
现在说别人出现并希望使用Printer类,并且自己的“Point”类声明如下:
class Pnt {
public:
double mX, mY;
// other stuff
};
如果他们试图这样做:
vector<Pnt> pnts; // Fill the pnts, and then...
Printer<Pnt> pntsPrinter(pnts);
pntsPrinter.Print(); // COMPILE ERROR HERE!
显然这会失败,因为Pnt没有x或y成员。有没有办法可以重写Printer类来处理所有通用用户类型?我不想做的是将Pnt向量复制到点向量中。
编辑:
我能想到的唯一方法是传递函数指针。像这样:
template<typename T>
class Printer {
public:
T* mData;
int mSize;
double* (*mXFunc) (T*);
double* (*mYFunc) (T*);
Printer(std::vector<T> &input,
double* (*xFunc) (T*),
double* (*yFunc) (T*))
{
mData = &input[0];
mSize = input.size();
mXFunc = xFunc;
mYFunc = yFunc;
}
void Print() {
printf(" - Showing %d items\n", mSize);
for (int i = 0; i < mSize; i++) {
T &item = mData[i];
printf(" - Item %d: (%lf, %lf)\n", i, *mXFunc(&item), *mYFunc(&item));
}
}
};
// Could then use it like so
inline double* getXPointVal(Point *point) {return &point->x;}
inline double* getYPointVal(Point *point) {return &point->y;}
inline double* getXPntVal(Pnt *point) {return &point->mX;}
inline double* getYPntVal(Pnt *point) {return &point->mY;}
Printer<Pnt> pntPrinter(pnts, getXPntVal, getYPntVal);
Printer<Point> pointsPrinter(points, getXPointVal, getYPointVal);
pntPrinter.Print();
pointsPrinter.Print();
这个问题是它看起来很丑陋,也可能引入了函数调用开销。但我想函数调用开销会被编译掉?我希望有更优雅的解决方案......
答案 0 :(得分:3)
如果您选择cout
而不是printf
来编写输出,则可以允许所有可打印类型为<<
运算符定义重载并在Printer::print()
内使用它}。重载可能如下所示:
std::ostream& operator<<(std::ostream &out, Point& p){
out << "Point(" << p.x << ", " << p.y << ")";
return out;
}
另外,我建议不要存储指向vector
内部存储和大小成员的指针。如果向量需要重新分配,则指针将悬空而无效。相反,您应该临时传递矢量作为参考或保留const参考。
答案 1 :(得分:0)
您可以为要使用的每个Point
类定义免费(非成员)函数。这样做的好处是可以在以后定义自由函数,而无需对现有类进行更改。
示例:
namespace A {
class Point {
public:
Point (int x, int y) : x_(x), y_(y) {}
int getX () const { return x_; }
int getY () const { return y_; }
private:
int x_, y_;
};
// in addition, we provide free functions
int getX (Point const & p) { return p.getX(); }
int getY (Point const & p) { return p.getY(); }
}
namespace B {
class Pnt {
public:
Pnt (int x, int y) : x_(x), y_(y) {}
int get_x () const { return x_; }
int get_y () const { return y_; }
private:
int x_, y_;
};
// Pnt does not have free functions, and suppose we
// do not want to add anything in namespace B
}
namespace PointHelpers {
// free functions for Pnt
int getX (Pnt const & p) { return p.get_x (); }
int getY (Pnt const & p) { return p.get_y (); }
}
// now we can write
template <class PointTy>
void printPoint (PointTy const & p) {
using PointHelpers::getX;
using PointHelpers::getY;
std::cout << getX (p) << "/" << getY (p) << std::endl;
}
A::Point p1 (2,3);
B::Pnt p2 (4,5);
printPoint (p1);
printPoint (p2);
如果自由函数与相应的类位于同一名称空间中,则可以通过参数依赖名称查找找到它们。如果您不想在该命名空间中添加任何内容,请创建一个帮助程序命名空间并在其中添加自由函数。然后通过using
声明将它们纳入范围。
此方法类似于STL对begin
和end
的处理方式。
答案 2 :(得分:0)
不要期望模板知道给定类/结构的哪些成员对应于您的x
和y
......
如果您想创建通用解决方案,您可以告诉您的打印机功能如何使用例如Point
类将给定对象解释为lambda表达式(c ++ 11解决方案):
#include <iostream>
class Point {
public:
double x, y;
Point() {x = y = 0;}
Point(double x, double y) {
this->x = x; this->y = y;
}
};
class Pnt {
public:
double mX, mY;
// other stuff
};
template <class P, class L>
void Print(const P &p, L l) {
Print(l(p));
}
void Print(const Point &p) {
std::cout << p.x << ", " << p.y << std::endl;
}
int main() {
Print(Point(1, 2));
Print(Pnt{4, 5}, [](const Pnt &p) -> Point {return Point(p.mX, p.mY);});
}