模板;点< 2,double&gt ;;点< 3,double>

时间:2010-05-06 07:08:10

标签: c++ templates

我想创建自己的Point结构,它只是为了学习C ++ 我有以下代码:

template <int dims, typename T>
struct Point {
    T X[dims];
    Point(){}
    Point( T X0, T X1 ) {
        X[0] = X0;
        X[1] = X1;
    }
    Point( T X0, T X1, T X2 ) {
        X[0] = X0;
        X[1] = X1;
        X[2] = X2;
    }
    Point<dims, int> toint() {
        //how to distinguish between 2D and 3D ???
        Point<dims, int> ret = Point<dims, int>( (int)X[0], (int)X[1]);
        return ret;
    }
    std::string str(){
        //how to distinguish between 2D and 3D ???
        std::stringstream s;
        s << "{ X0: " << X[0] << " | X1: " <<  X[1] << " }";
        return s.str();
    }
};
int main(void) {

    Point<2, double> p2d = Point<2, double>( 12.3, 45.6 );
    Point<3, double> p3d = Point<3, double>( 12.3, 45.6, 78.9 );

    Point<2, int> p2i = p2d.toint(); //OK
    Point<3, int> p3i = p3d.toint(); //m???

    std::cout << p2d.str() << std::endl; //OK
    std::cout << p3d.str() << std::endl; //m???
    std::cout << p2i.str() << std::endl; //m???
    std::cout << p3i.str() << std::endl; //m???

    char c; std::cin >> c;
    return 0;
}
到目前为止输出不是我想要的。 我的问题是: 如何在Point的成员函数中处理Point(2D或3D)的尺寸?

非常感谢提前祝贺 糟糕

6 个答案:

答案 0 :(得分:6)

您的维度在编译时由模板arguemnt dims修复,因此您可以迭代它们:

std::string str(){
    //how to distinguish between 2D and 3D ???
    std::stringstream s;
    s << "{ ";
    std::copy( X, X+dims, std::ostream_iterator<T>( s, "|") );
    s << " }";
    return s.str();    
}

此外,您可以根据dims

提供构造函数
Point( const T (&c) [dims] ){
  std::copy( c, c+dims, X );
}

这允许像double[] d={1,2,3}; Point<3,double> p(d);这样的代码,它可以决定其他地方的参数数量。

对于'cast-to-point-of-ints'构造函数,您可以使用Point作为参数:

Point<dims,int> toint() const {
    Point<dims,int> result;
    std::copy( X, X+dims, result.X );
    return result;
}

答案 1 :(得分:3)

不应对分配进行硬编码,而应简单地使用循环。例如:

std::string str(){
    if ( dims > 0 ){
        std::stringstream s;
        s << "{ X0: " << X[0];
        for ( int i = 1; i < dims; i++ ){
            s << " | X" << i << ": " <<  X[i];
        }
        s <<" }";
        return s.str();
    }
    return "{}";
}

但是,就构造函数而言,您必须专门化模板以使控件具有正确数量的参数,否则您将不得不等待C ++ 0x使用初始化程序列表,或者您只需必须解决一个功能稍弱的语法,你传入一个初始化数组。但是,对于所有其他函数,您可以使用for循环,使用dims作为点的维度。我还应该指出boost::array类可能已经提供了您需要的功能,如果没有,建议您在该类之上构建所需的任何功能,因为它与您拥有的功能非常相似。 / p>

另外,另外,您应该提供一个典型的ostream运算符&lt;&lt;你的类的函数,并且可选地实现str()函数,因为你已经在str()的实现中有效地实现了它,但是提供操作符重载比简单地提供str()更灵活和强大。您可能也对boost::lexical_cast感兴趣,因为它可以对提供ostream运算符的任何对象进行字符串化。

答案 2 :(得分:2)

正如其他人所说,只需使用how to distinguish between 2D and 3D?作为变量即可回答dims

更有趣的问题是如何使构造函数表现得只能调用正确的构造函数。一个替代方案是boost::enable_if,如果不满足条件,它会生成一个有目的的编译器错误,基本上取消函数声明。

template <int dims, typename T>
struct Point {
    T X[dims];
    Point(){} // should this really be empty?
    Point( typename enable_if_c< dims == 2, T >::type X0, T X1 ) {
        X[0] = X0;
        X[1] = X1;
    }
    Point( typename enable_if_c< dims == 3, T >::type X0, T X1, T X2 ) {
        X[0] = X0;
        X[1] = X1;
        X[2] = X2;
    }

这种技术通常称为SFINAE,因为替换失败不是错误(子文本:编译器可能会尝试另一次重载),但在这种情况下,我们确实想要错误。

另一种方法是提供一个变量类型和默认参数来有选择地禁用第三个参数,或者强制指定它:

template< int dims, typename T >
class tweak_point_ctor_argument; // disallow general case of dims != 2,3

template< typename T >
class tweak_point_ctor_argument< 2, T > {
    tweak_point_ctor_argument() {} // private constructor
    typedef tweak_point_ctor_argument type;
    operator int() { return 0; }
    friend struct Point;
};

template< typename T >
class tweak_point_ctor_argument< 3, T > {
    typedef T type;
    friend struct Point;
};

    …
    Point( T X0, T X1, 
        typename tweak_point_ctor_argument< dims, T >::type X2
            = tweak_point_ctor_argument<dims, T>() ) {
        X[0] = X0;
        X[1] = X1;
        X[2] = X2;
    }

如果dims为2,则::type计算为tweak_point_ctor_argument< 2, T >,除默认参数外,不能构造dims。因此用户不可能使用第三个参数调用构造函数。如果::type为3,则T的计算结果为enable_if,导致默认参数初始值设定项无效,从而强制用户不使用它。

我还没有测试过这段代码,它只是演示了解决问题的各种方法,{{1}}是最好的方法。

答案 3 :(得分:1)

添加成员变量:

template <int dims, typename T>
struct Point {
    //whatever
    static const int Dimensions = dims;
};

现在您可以在成员函数和外部代码中使用它(如果您当然不更改其访问限制)。

答案 4 :(得分:1)

如果您希望您的成员方法知道维度(您的“dims”模板参数), 将以下行添加到模板类中:

static const int Dimension = dims;

然后在您的方法中使用Dimension。这是一个静态const成员变量,因此编译器将能够对其进行优化,而不需要任何存储空间。

我刚注意到你也可以在成员函数中直接使用标识符“dims”:

public void PrintDimensions()
{ 
     std::cout << dims;
}

适用于我的编译器。

答案 5 :(得分:1)

如何为Point2D创建一个类,为Point3D创建一个类并将这些类包装到Point类中?

struct nil;

class PointBase {
 /* ... */
 PointBase();
 PointBase(const PointBase&);
 virtual ~PointBase();
}

template<class T_arg1, class T_arg2>
class Point2D : public PointBase {
 /* ... */
 Point2D(T_arg1 arg1, T_arg2 arg);
 Point2D(const Point2D& src) : PointBase(src) {}
 virtual ~Point3D();
};


template<class T_arg1, class T_arg2, class T_arg3>
class Point3D : public PointBase {
 /* ... */
 Point3D(T_arg1 arg1, T_arg2 arg2, T_arg3 arg3);
 Point3D(const Point3D& src) : PointBase(src) {}
 virtual ~Point3D();
};

template<class T_arg1, class T_arg2, class T_arg3 = nil>
class Point : public Point3D<T_arg1, T_arg2, T_arg3> {
 /* ... */
 Point(T_arg1 arg1, T_arg2 arg2, T_arg3 arg3)
  : Point3D<T_arg1, T_arg2, T_arg3>(arg1, arg2, arg3) {} 
 Point(const Point& src) : Point3D<T_arg1, T_arg2, T_arg3>(src) {}
 virtual ~Point();
}

template<class T_arg1, class T_arg2>
class Point<T_arg1, T_arg2, nil> : public Point2D<T_arg1, T_arg2> {
 /* ... */
 Point(T_arg1 arg1, T_arg2 arg2)
  : Point2D<T_arg1, T_arg2>(arg1, arg2) {} 
 Point(const Point& src) : Point2D<T_arg1, T_arg2>(src) {}
 virtual ~Point();
}

然后使用:

Point<double, double> my2DPoint(0.0, 0.0);
Point<double, double, double> my3DPoint(0.0, 0.0, 0.0);