Operator konwersji

0

Jak poprawnie zdefiniować operator konwersji w takim przypadku?

template< typename T >
class Box
{
  private:
    T* instance;

  public:
    Box() : instance( new T ) { }
    ~Box() { delete instance; }
    operator const T*() const { return instance; }
};

struct Foo
{
  int value;
  float value2;
};

void Func( const Foo* foo )
{
  cout << " value: " << foo->value << "\nvalue2: " << foo->value2 << "\n";
}

int main( int, char** )
{
  Box< Foo > box* = new Box< Foo >;
  Func( box );    // <- wrzeszczy o niemożność konwersji z Box<Foo>* na const Foo*
  return 0;
}
1

Box< Foo > box* ⟵ zakładam, że to miał być wskaźnik na Box<Foo>. Wskaźnik nie jest Twoim typem. Nie możesz dla wskaźnika zdefiniować funkcji konwertującej.

Bezpośrednim uśmierzeniem bólu będzie użycie box jako zmiennej o automatycznym czasie życia:

Box<foo> box;
Func(box);

Jednak taki kod nie będzie dobry (zakładam, że piszesz własną implementację std::unique_ptr w celach edukacyjnych) z następujących powodów:

  • konwersja jest niejawna (explicit is better than implicit),
  • pisanie wszędzie new i delete miało sens w poprzednim tysiącleciu - czyli już dobre kilka lat temu (to odnośnie samej zmiennej box, bo do implementacji smart pointera potrzeba),
  • nie bierzesz pod uwagę rule of three/five (zero nie ma zastosowania).
2
Func(*box);
1

Najlepiej tego NIE ROBIĆ wcale!
A jeszcze lepiej wywalić do kosza Box i użyć std::unique_ptr (tam nie zdefiniowali takiego operatora, ale jest metoda get, która pełni tą rolę).

0

PS: dlaczego używasz tyle nagich new i delete? 1995 był 22 lata temu.

A jeszcze lepiej wywalić do kosza Box i użyć std::unique_ptr (tam nie zdefiniowali takiego operatora, ale jest metoda get, która pełni tą rolę).

Dziwne rady. Przecież widać, że próbuje napisać własny smart pointer.

Box< Foo > box* = new Box< Foo >;
Func( box ); // <- wrzeszczy o niemożność konwersji z Box<Foo>* na const Foo*


Bo powinno być

````cpp
  Box< Foo > box;
  Func( box );
0
kq napisał(a):

Box< Foo > box* ⟵ zakładam, że to miał być wskaźnik na Box<Foo>. Wskaźnik nie jest Twoim typem. Nie możesz dla wskaźnika zdefiniować funkcji konwertującej.

Oczywiście. Literówka.

Nie możesz dla wskaźnika zdefiniować funkcji konwertującej.

Ale explicit chyba można? Tak żeby static_cast zadziałał mimo braku relacji między klasami.

  • pisanie wszędzie new i delete miało sens w poprzednim tysiącleciu - czyli już dobre kilka lat temu (to odnośnie samej zmiennej box, bo do implementacji smart pointera potrzeba),

Jasna sprawa. Gołe new/deleteużywam tylko w takich mikro-przykładach.

twonek napisał(a):
Func(*box);

Na to bym nie wpadł.

MarekR22 napisał(a):

Najlepiej tego NIE ROBIĆ wcale!
A jeszcze lepiej wywalić do kosza Box i użyć std::unique_ptr (tam nie zdefiniowali takiego operatora, ale jest metoda get, która pełni tą rolę).

Ogółem problem polega na tym, że chciałbym w Qt zdefiniować wspólne zachowanie dla różnego rodzaju 'widgetów'. Niezbyt podobała mi się opcja z tworzeniem kolejnych klas dziedziczących a to po QPushButton, a to po QLineEdit, etc i przeklejaniem tego samego kodu. Dlatego wpadłem na pomysł, żeby zrobić to raz jako klasę szablonową, która łapałaby odpowiednie eventy i je obsługiwała.

class UIElementBase
{
  protected:
    QWidget* nativeWidget;

  public:
    UIElementBase() : nativeWidget( nullptr ) { }
    QWidget* GetNativeWidget() const { return nativeWidget; }
    operator QWidget*() const { return nativeWidget; }

};

template< typename T >
class UIElement : public QObject, public UIElementBase
{
  Q_OBJECT

  protected:
    QWidget* nativeWidget;
    bool event( QWidget* widget, QEvent* event );
    void OnEventFocusIn();
    void OnEventFocusOut();
    void OnEventHoverIn();
    /* itd. */

  public:
    UIElement( UIElementBase* parent = Q_NULLPTR )
    {
      nativeWidget = new T( parent ? parent : nullptr );
      nativeWidget->installEventFilter( this );
    }
};

typedef UIElement< QPushButton > UIButton;
typedef UIElement< QLineEdit > UILineEdit;
typedef UIElement< QLabel > UILabel;

Spodobało mi się takie rozwiązanie, tylko było to dość upierdliwe przy korzystaniu z funkcji Qt, np. przy layoutach. Musiałbym wszędzie klepać coś w stylu

QHBoxLayout* layout = new QHBoxLayout;
UIButton* btn = new UIButton;
layout->addWidget( btn->GetNativeWidget() );

Stąd pomysł konwersji implicit żeby używać tego naturalny sposób:

layout->addWidget( btn );

Chyba, że jest jakiś lepszy sposób na zdefiniowane wspólnych zachowań dla pewnej grupy klas :P

1

Krótko: przekombinowałeś ponad miarę.

class MyEventFilterDoingStrangeStuff : public QObject
{
    Q_OBJECT
public:
    explicit MyEventFilterDoingStrangeStuff(QWidget *parent) : QObject(parent)
    {
           parent->installEventFilter(this);
    }

protected:
    bool eventFilter(QObject *obj, QEvent *event)
    {
           if (event->type() == QEvent::KeyPress)
           {
                 // psuj cokolwiek potrzebujesz popsuć
                return false;
           }
           return QObject::eventFilter(obj, event);
    }
};

I teraz na ten obiekt doklejasz do widgeta,

new MyEventFilterDoingStrangeStuff(widget);

i całe zarządzanie pamięcią robi się samo.

Swoją drogą to jest klasyczny przykład problemu XY.
Masz jakiś prosty problem, który rozwiązujesz w dziwny sposób i prosisz o naprawianie udziwnienia, zamiast wyjaśnić jaką funkcjonalność chcesz osiągnąć.
Zresztą sam nie zdziwiłbym się gdyby nie było jeszcze jednej warstwy twojego problemu.

0
MarekR22 napisał(a):

Krótko: przekombinowałeś ponad miarę.

Nie pierwszy raz. Wiem, że mam do tego skłonności i staram się z tym walczyć, ale jak widać nie zawsze wychodzi :P

class MyEventFilterDoingStrangeStuff : public QObject
{
    Q_OBJECT
public:
    explicit MyEventFilterDoingStrangeStuff(QWidget *parent) : QObject(parent)
    {
           parent->installEventFilter(this);
    }

protected:
    bool eventFilter(QObject *obj, QEvent *event)
    {
           if (event->type() == QEvent::KeyPress)
           {
                 // psuj cokolwiek potrzebujesz popsuć
                return false;
           }
           return QObject::eventFilter(obj, event);
    }
};

I teraz na ten obiekt doklejasz do widgeta,

new MyEventFilterDoingStrangeStuff(widget);

i całe zarządzanie pamięcią robi się samo.

Swoją drogą to jest klasyczny przykład problemu XY.
Masz jakiś prosty problem, który rozwiązujesz w dziwny sposób i prosisz o naprawianie udziwnienia, zamiast wyjaśnić jaką funkcjonalność chcesz osiągnąć.

Głównie oczywiście problem dotyczy konkretnie Qt, ale tak czy siak chciałem się dowiedzieć jak takie coś zrobić ogólnie, bez skupiania się na konkretnym frameworku.

Zresztą sam nie zdziwiłbym się gdyby nie było jeszcze jednej warstwy twojego problemu.

Pewnie "w praniu" wyszłaby jeszcze nie jedna zagwozdka ;)

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