template spezialisierung



  • Hallo, ich habe eine Frage für template Experten unter euch:

    Folgender Code:

    template< class ContainerType > class container_resizer
        {
        public:
    	void resize( const ContainerType &x , ContainerType &dxdt ) const
            {
    	    if( x.size() != dxdt.size() ) dxdt.resize( x.size() );
    	}
        };
    
        template< class ContainerType , class ResizerType = container_resizer<ContainerType> >
        class ode_step_euler
        {
    	ContainerType dxdt;
    	ResizerType resizer;
        public:
    
    	template< class DynamicalSystem , class TimeType>
    	void next_step( DynamicalSystem system , ContainerType &x , TimeType t , TimeType dt )
            {
    	    resizer.resize( x , dxdt );
    	    system( x , dxdt , t );
    	    ContainerType::iterator state_begin = x.begin();
    	    ContainerType::iterator state_end = x.end();
    	    ContainerType::iterator derivative_begin = dxdt.begin();
    	    while( state_begin != state_end ) (*state_begin++) += dt * (*derivative_begin++);
    	}
        };
    

    Der funktioniert soweit auch und ich kann den Code zum Beispiel so benutzen:

    ode_step_euler< vector<double> > euler;
    

    Ich möchte nun aber die resizer Klasse auf std::tr1::array<T,N> spezialisieren:

    template< class T , int N > class container_resizer< std::tr1::array<T,N> >
        {
        public:
            void resize( const std::tr1::array<T,N> &x , std::tr1::array<T,N> &dxdt ) { }
        };
    

    Dann sind solche Konstrukte wie

    ode_step_euler< array<double,3> > euler;
    

    leider nicht möglich. Nur wenn ich einen expliziten Resizer schreibe und den auch via

    ode_step_euler< array<double,3> , array_resizer< array<double,3> > > euler;
    

    angeben. Gibt es eine Möglichkeit soetwas zum umgehen?



  • headmyshoulder schrieb:

    Dann sind solche Konstrukte wie

    ode_step_euler< array<double,3> > euler;
    

    leider nicht möglich.

    Weshalb nicht? Ich seh das Problem nicht gerade.

    Übrigens: Ich würde die Schnittstelle wenn möglich bei Spezialisierungen und allgemeinem Template konsistent halten. Das betrifft das const bei resize() .



  • Nexus schrieb:

    headmyshoulder schrieb:

    Dann sind solche Konstrukte wie

    ode_step_euler< array<double,3> > euler;
    

    leider nicht möglich.

    Weshalb nicht? Ich seh das Problem nicht gerade.

    Ok, hier ist mal der Code, bei dem das Compilieren fehlschlägt.

    #include <vector>
    #include <tr1/array>
    
    using namespace std;
    using namespace std::tr1;
    
    // Resizer
    template< class ContainerType > class container_resizer
    {
    public:
        void resize( const ContainerType &x , ContainerType &dxdt ) const
        {
            if( x.size() != dxdt.size() ) dxdt.resize( x.size() );
        }
    };
    
    template< class T , int N > class container_resizer< array< T , N > >
    {
    public:
        void resize( const array<T,N> &x , array<T,N> &dxdt ) const
        {
        }
    };
    
    // Stepper
    template< class ContainerType ,	class ResizerType = container_resizer<ContainerType> >
    class ode_step_euler
    {
        ContainerType dxdt;
        ResizerType resizer;
    
    public:
    
        template< class DynamicalSystem , class TimeType >
        void next_step( DynamicalSystem system , ContainerType &x , TimeType t , TimeType dt )
        {
    	resizer.resize( x , dxdt );
    	system( x , dxdt , t );
    	typename ContainerType::iterator state_begin = x.begin();
    	typename ContainerType::iterator state_end = x.end();
    	typename ContainerType::iterator derivative_begin = dxdt.begin();
    	while( state_begin != state_end ) (*state_begin++) += dt * (*derivative_begin++);
        }
    };
    
    // definition of the system
    void lorenz_vector( const vector<double> &x , vector<double> &dxdt , double t )
    {
        dxdt[0] = 10.0 * ( x[1] - x[0] );
        dxdt[1] = 28.0 * x[0] - x[1] - x[0] * x[2];
        dxdt[2] = x[0]*x[1] - 2.6666666666 * x[2];
    }
    
    // definition of the system
    void lorenz_array( const array< double , 3 > &x , array< double , 3 > &dxdt , double t )
    {
        dxdt[0] = 10.0 * ( x[1] - x[0] );
        dxdt[1] = 28.0 * x[0] - x[1] - x[0] * x[2];
        dxdt[2] = x[0]*x[1] - 2.6666666666 * x[2];
    }
    
    int main()
    {
        ode_step_euler< vector<double> > euler_vector;
        ode_step_euler< array< double , 3 > > euler_array;
        vector<double> xv(3);
        array<double,3> xa;
    
        euler_vector.next_step( lorenz_vector , xv , 0.0 , 0.01 );
        euler_array.next_step( lorenz_array , xa , 0.0 , 0.01 );
    }
    

    Dann liefert die Ausgabe von g++

    main.cc: In member function ‘void container_resizer<ContainerType>::resize(const ContainerType&, ContainerType&) const [with ContainerType = std::tr1::array<double, 3ul>]’:
    main.cc:46:   instantiated from ‘void ode_step_euler<ContainerType, ResizerType>::next_step(DynamicalSystem, ContainerType&, TimeType, TimeType) [with DynamicalSystem = void (*)(const std::tr1::array<double, 3ul>&, std::tr1::array<double, 3ul>&, double), TimeType = double, ContainerType = std::tr1::array<double, 3ul>, ResizerType = container_resizer<std::tr1::array<double, 3ul> >]’
    main.cc:81:   instantiated from here
    main.cc:13: error: ‘struct std::tr1::array<double, 3ul>’ has no member named ‘resize’
    

    Und man sieht, daß beim Anlegen von euler_array nicht die array<T,N> Spezialisierung verwendet wurde, sondern der Standard-Resizer. Man spezialiert die Resizer Klasse wieder mit einer Template Klasse und das führt zu dem Fehler.

    Man kann natürlich einen Resizer nur für array 's schreiben, wie

    template< class T , int N > class array_resizer
    {
    public:
        void resize( const array<T,N> &x , array<T,N> &dxdt ) const
        {
        }
    };
    

    und diesen dann direkt beim Anlegen des Array Steppers angeben:

    ode_step_euler< array<double,3> , array_resizer<double,3> > euler_array;
    

    aber das ist halt nicht so user-freundlich. Es wäre schöner, wenn der Compiler entscheiden würde, welcher Resizer verwendet wird.



  • Sehr merkwürdig. Auf MSVC++ kompiliert der Code einwandfrei. Es wird auch wie erwartet die Array-Version aufgerufen.

    Versuch mal, den Templateparameter für die Grösse bei der Spezialisierung als size_t statt int zu deklarieren.



  • Nexus schrieb:

    Sehr merkwürdig. Auf MSVC++ kompiliert der Code einwandfrei. Es wird auch wie erwartet die Array-Version aufgerufen.

    Versuch mal, den Templateparameter für die Grösse bei der Spezialisierung als size_t statt int zu deklarieren.

    Cool, danke damit funktionierts. Wenn ich das richtig interpretiere ist dann ein array< class T , int N> was anderes als array< class T , size_t N > .



  • headmyshoulder schrieb:

    Wenn ich das richtig interpretiere ist dann ein array< class T , int N> was anderes als array< class T , size_t N > .

    Ja, das sind zwei verschiedene Templates. Visual C++ scheint es hier aber nicht so genau zu nehmen.


Anmelden zum Antworten