我想创建我自己的Point struct,它只是为了学习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)?
非常感谢你提前
哦哦
发布于 2010-05-06 15:15:20
你的维度在编译时是固定的,通过模板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-int’构造函数,你可以使用Point作为参数:
Point<dims,int> toint() const {
Point<dims,int> result;
std::copy( X, X+dims, result.X );
return result;
}发布于 2010-05-06 15:20:06
您应该简单地使用循环,而不是硬编码赋值。例如:
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类可能已经提供了您需要的功能,如果没有,建议在该类的基础上构建您需要的任何功能,因为它与您所拥有的非常相似。
另外,您应该为您的类提供一个典型的ostream str函数,并且可以选择在此函数之上实现operator<< ()函数,因为您已经在您的str()实现中有效地实现了它,但是提供操作符重载比简单地提供str()更加灵活和强大。您也可能对boost::lexical_cast感兴趣,因为它可以将任何提供ostream操作符的对象字符串化为字符串。
发布于 2010-05-06 17:45:57
正如其他人所说,只需使用dims作为变量即可回答how to distinguish between 2D and 3D?。
更有趣的问题是如何让构造器的行为使得你只能调用正确的构造器。一种替代方法是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为3,则::type的计算结果为T,导致默认参数初始值设定项无效,从而强制用户不使用它。
我还没有测试过这段代码,它只是演示了解决问题的各种方法,enable_if在这里是更好的方法。
https://stackoverflow.com/questions/2779155
复制相似问题