Automatyczna inicjalizacja statycznego obiektu w klasie korzystającej z szablonów

0

Witam!

Na wstępie zaznaczę, że moje słownictwo może nie być technicznie poprawne, za co z góry przepraszam.

Problem został przedstawiony w nazwie tematu. Dodam tylko, że ów problem został już rozwiązany, kiedy szablony nie były używane. Czyli kod, który przedstawię, działał tak jak powinien bez szablonów.

Na potrzeby tematu stworzyłem abstrakcyjną sytuację. Mamy klasę rodzica IParent oraz klasę potomną Child. Rodzic przechowuje piłkę, która jest udostępniona dla dzieci. Owa piłka jest tylko jedna i będą grały nią wszystkie dzieci. Wszystko pięknie, tylko piłka na samym początku jest pozbawiona powietrza i trzeba ją jakoś napompować.

Proces pompowania ma się odbyć tylko raz, zaraz po ulokowaniu piłki u rodzica, i co więcej - ma się odbyć samoistnie.

Mam nadzieję, że poniższy kod rozjaśni sytuację.

Parent.hpp

#ifndef PARENT_HPP
#define PARENT_HPP

#include "Ball.hpp"
#include <iostream>


template< typename T >
class IParent {
public:

    virtual T GoalsNumber( void ) = 0;


protected:
    
    static Ball ms_ball;

    static class BallPump {
    public:
        BallPump( void ) {
            ms_ball.Inflate();
            
            std::cout << "Ball is inflated now!\n";
        }
    } ms_ballPump;
};


template< typename T >
Ball IParent< T >:: ms_ball;


template< typename T >
typename IParent< T >::BallPump IParent< T >::ms_ballPump;


#endif

Child.hpp

#ifndef CHILD_HPP
#define CHILD_HPP

#include "Parent.hpp"


template< typename T >
class Child : IParent< T > {
public:

    T GoalsNumber( void ) {}
};


#endif

Ball.hpp

#ifndef BALL_HPP
#define BALL_HPP


class Ball {
public:
    enum Inflation { inflated, deflated };
    
    Ball( void ) : m_inflation( deflated ) {}

    void Inflate( void ) { m_inflation = inflated; }
    
private:

    Inflation m_inflation;
};


#endif

main.cpp

#include "Child.hpp"

using namespace std;


int main( int argc, char** argv ) {
    Child< short > Marcin;
    Child< short > Maciek;

    return 0;
}

Nie miałem pomysłu, jak wpleść w to logicznie szablony, więc w podanym przykładzie dziecko będzie wyrażało liczbę strzelonych przez siebie goli w podanym typie (w przykładzie jest to short). Za automatyczne pompowanie piłki odpowiada statyczny obiekt pomocniczej klasy BallPump, znajdujący się w klasie IParent. Konstruktor ów klasy pompuje piłkę i wyświetla informację, że operacja się powiodła. Niestety w przypadku z szablonami, czyli po skompilowaniu i uruchomieniu powyższego kodu, konstruktor klasy BallPump nie zostaje wywołany i informacja nie jest wyświetlana.

Pod klasą IParent znajdują się następujące dwie linijki:

template< typename T >
typename IParent< T >::BallPump IParent< T >::ms_ballPump;

Sugerują one stworzenie obiektu klasy BallPump, jednak obiekt nie jest tworzony, przynajmniej nie przy starcie programu. Może jest coś nie tak właśnie w tym miejscu, mimo, że identyczny zapis, tylko bez użycia szablonów, kończy się sukcesem?

4

Nigdzie w kodzie nie odwołujesz się do tego statycznego elementu klasy, więc kompilator w czasie podstawiania szablonu go nie generuje. Masz 3 opcje do wyboru:

  1. Napisać to lepiej, singletony to prawie zawsze zło.
  2. Wymusić utworzenie obiektu dla danego typu
template<>
typename IParent<short>::BallPump IParent<short>::ms_ballPump{};
  1. Wymusić utworzenie tego obiektu poprzez użycie funkcji, która się do niego odwołuje, np.:
template< typename T >
class IParent {
public:

	virtual T GoalsNumber( void ) = 0;

	IParent(){ &ms_ballPump; } // no-op, ale kompilator widząc odwołanie do obiektu musi dokonać utworzenia klasy z szablonu

protected:

	static Ball ms_ball;

	static class BallPump {
	public:
		BallPump( void ) {
			ms_ball.Inflate();

			std::cout << "Ball is inflated now!\n";
		}
	} ms_ballPump;
};
0

Super, dwa ostatnie sposoby działają. Osobiście bardziej przypadł mi do gustu trzeci sposób, wydaje mi się bardziej "elegancki" :D Co do drugiego sposobu, to trzeba jeszcze zaznaczyć, że owe wymuszenie należy przenieść do pliku .cpp, inaczej kompilator się zbuntuje krzycząc o wielokrotnej definicji IParent< short >::ms_ballPump.

Co do pierwszej opcji, mógłbyś wskazać jakieś konkretne podejście?

1 użytkowników online, w tym zalogowanych: 0, gości: 1