开发者

template class: ctor against function -> new C++ standard

in this question:

template; Point<2, double>; Point<3, double>

Dennis and Michael noticed the unreasonable foolishly implemented constructor.

They were right, I didn't consider this at that moment. But I found out that a constructor does not help very much for a template class like this one, instead a function is here much more convenient and safe

namespace point {

template < unsigned int dims, typename T >
struct Point {

    T X[ dims ];

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

    Point<dims, int> toint() {
        Point开发者_StackOverflow<dims, int> ret;
        std::copy( X, X+dims, ret.X );
        return ret;
    }
};

template < typename T >
Point< 2, T > Create( T X0, T X1 ) {
    Point< 2, T > ret;
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1;
    return ret;
}
template < typename T >
Point< 3, T > Create( T X0, T X1, T X2 ) {
    Point< 3, T > ret;
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2;
    return ret;
}
template < typename T >
Point< 4, T > Create( T X0, T X1, T X2, T X3 ) {
    Point< 4, T > ret;
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2; ret.X[ 3 ] = X3;
    return ret;
}
};
int main( void ) {
    using namespace point;
    Point< 2, double > p2d = point::Create( 12.3, 34.5 );
    Point< 3, double > p3d = point::Create( 12.3, 34.5, 56.7 );
    Point< 4, double > p4d = point::Create( 12.3, 34.5, 56.7, 78.9 );
    //Point< 3, double > p1d = point::Create( 12.3, 34.5 ); //no suitable user defined conversion exists

    //Point< 3, int > p1i = p4d.toint(); //no suitable user defined conversion exists
    Point< 2, int > p2i = p2d.toint();
    Point< 3, int > p3i = p3d.toint();
    Point< 4, int > p4i = p4d.toint();

    std::cout << p2d.str() << std::endl;
    std::cout << p3d.str() << std::endl;
    std::cout << p4d.str() << std::endl;
    std::cout << p2i.str() << std::endl;
    std::cout << p3i.str() << std::endl;
    std::cout << p4i.str() << std::endl;

    char c;
    std::cin >> c;
}  

has the new C++ standard any new improvements, language features or simplifications regarding this aspect of ctor of a template class?

what do you think about the implementation of the combination of namespace, stuct and Create function?

many thanks in advance

Oops


Since the array is public, it is an option to omit the constructor and allow aggregate initialization (like boost::array<T, N> for example).

Point<2, int> p = {1, 2};

This is no worse than having to call a create function. (The create function might still be handy as a utility.)


In C++0x you will be able to have all sorts of coolness. For example, play with variadic templates, to have it checked at compile-time if the constructor is called with a right number of arguments. (The following could also check if the arguments ...U are all of type T, with some more metaprogramming fun, but it might not be absolutely necessary.)

//helper to copy variable amount of arguments into an array
namespace detail {

template <class T, class U>
void copy_variadic(T* p, U value)
{
    *p = value;
}

template <class T, class First, class ...Rest>
void copy_variadic(T* p, First var, Rest ...args)
{
    *p = var;
    copy_variadic(++p, args...);
}
} //detail

template < unsigned int dims, typename T >
struct Point {

    T X[ dims ];

    Point() : X{}
    {
    }

    template <class ...U>
    Point(U... args) {
        static_assert(sizeof...(args) == dims, "Too many or too few arguments to Point constructor");
        detail::copy_variadic(X, args...);
    }
    //...
};

(Actually, with some modifications - perfect forwarding - copy_variadic would make a nice addition to my collection of variadic-template utilities, if someone doesn't come and point out a significantly better way.)


Yes, as Michael pointed out in his answer to your previous question, in C++0x you'll be able to use an initializer list to pass an arbitrary number of arguments to your ctor. In your case, the code would look something like:

template <int dims, class T>
class point { 
    T X[dims];
public:
    point(std::initializer_list<T> const &init) { 
        std::copy(init.begin(), init.begin()+dims, X);
    }
};

You could create a point object with this something like:

point<3, double> x{0.0, 0.0, 0.0};

Personally, I'm not sure I like the basic design very well though. In particular, I'd rather see X turned into an std::vector, and determine the number of dimensions strictly from the parameter list that was passed instead of having it as a template argument:

template <class T>
class point { 
    std::vector<T> X;
public:
    point(std::initializer_list<T> init) {
        std::copy(init.begin(), init.end(), std::back_inserter(X));
    }
};

This does have some trade-offs though -- points with a different number of dimensions are still the same type. For example, it basically asserts that it's reasonable to assign a 2D point to a 3D point, or vice versa.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜