使用通用用户定义的输入使c ++类工作

时间:2016-07-19 19:04:00

标签: c++ linux templates

我觉得这个问题之前一定是问过,但我找不到谷歌上的答案。如果有,请指示我链接,我将删除此帖。

考虑这个代表我遇到的更大问题的最小例子。假设我创建了一个简单的“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();    

这个问题是它看起来很丑陋,也可能引入了函数调用开销。但我想函数调用开销会被编译掉?我希望有更优雅的解决方案......

3 个答案:

答案 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对beginend的处理方式。

答案 2 :(得分:0)

不要期望模板知道给定类/结构的哪些成员对应于您的xy ......

如果您想创建通用解决方案,您可以告诉您的打印机功能如何使用例如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);});
}