Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

1

10.03.2016, 23:02

Const Deduktion in C++?

Moin. Im D Forum geht gerade ein Vergleich zwischen D und C++ um, und da wollte ich doch glatt mal um Rat fragen. Die C++ Gurus unter euch können mir sicher sagen, ob es einen Weg gibt, const als Modifier zu ermitteln. Zum besseren Verständnis mal der D Code:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import std.stdio : writeln;

class Visitor {
public:
    void visit(inout A a) {
        writeln("visit A");
    }

    void visit(inout B b) {
        writeln("visit B");
    }
}

class A {
public:
    void accept(Visitor v) inout {
        v.visit(this);
    }
}

class B : A {
public:
    override void accept(Visitor v) inout {
        v.visit(this);
    }
}

void main() {
    Visitor v = new Visitor();
    
    const A a = new A();
    A b = new B();

    a.accept(v);
    b.accept(v);
}


inout ist ein "wildcard", dass automatisch const deduziert, sofern der Typ const ist. Andernfalls bleibt der Typ, wie beschrieben.

Das gleiche in C++ zu erreichen, ist etwas umständlicher. Jedenfalls das Konstrukt, was mir als Lösung einfiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <type_traits>
#include <memory>

class A;
class B;

enum class State {
    Mutable,
    Immutable
};

template <State S>
class Visitor {
public:
    using A_T = typename std::conditional<S == State::Immutable, const A, A>::type;
    using B_T = typename std::conditional<S == State::Immutable, const B, B>::type;
    
    void visit(A_T* a) {
        std::cout << "visit A" << std::endl;
    }

    void visit(B_T* b) {
        std::cout << "visit B" << std::endl;
    }
};

class A {
public:
    virtual void accept(Visitor<State::Mutable>& mv) {
        mv.visit(this);
    }
    
    virtual void accept(Visitor<State::Immutable>& iv) const {
        iv.visit(this);
    }
};

class B : public A {
public:
    virtual void accept(Visitor<State::Mutable>& mv) {
        mv.visit(this);
    }
    
    virtual void accept(Visitor<State::Immutable>& iv) const {
        iv.visit(this);
    }
};

int main() {
    std::unique_ptr<const A> a(new A());
    std::unique_ptr<A> b(new B());
    
    Visitor<State::Mutable> mv;
    Visitor<State::Immutable> iv;

    a->accept(iv);
    b->accept(mv);
}


Nun meine Frage: Geht das schöner/besser/eleganter/kürzer? :P
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

11.03.2016, 09:03

Ich versteh's inhaltlich nicht so recht. Willst Du den Parameter nun const oder willst Du ihn verändern dürfen? Beides zu wollen, je nachdem, was ich übergebe, klingt für mich etwas merkwürdig. Zudem, wenn Du in C++ eh schon zwei Methoden hast, wieso dann so kompliziert und nicht einfach über const S& und S&, da wird schließlich auch das genommen, was am besten passt. Wozu der grauenvolle Weg über das Template?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

3

11.03.2016, 10:03

Ich will nur sehen, inwiefern ich das Verhalten von D nachäffen kann. Also möchte ich dynamisch eine Methode, die sowohl const A* als auch nur A* akzeptiert. Dazu muss in in D inoutverwenden und in C++ (jedenfalls bei mir da oben) einen template Visitor. Grundsätzlich geht's darum, den Mehraufwand zu vermeiden, sowohl void visit(const A*) als auch void visit(A*) zu implementieren. Ich glaub aber, der Visitor als Beispiel ist ziemlich schrecklich gewählt. ^^
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

4

11.03.2016, 10:33

Mir ist auch nicht ganz klar, was genau du erreichen willst. Nach deinem Beispiel würde ich vermuten, dass es dir darum geht, dass der Visitor ein const Bla* bekommt wenn das Bla const war und sonst ein Bla*!?

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

5

11.03.2016, 11:03

Richtig. Das ermöglicht das inout von D dynamisch. Und ich will sehen inwieweit man das in C++ adaptieren kann.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

6

11.03.2016, 11:12

Ich fürchte das geht nicht so einfach, zumindest nicht wenn Polymorphismus im Spiel ist. Die Frage ist: Was genau ist das für ein Visitor, der zwischen const und non const Subject unterscheiden muss?

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

7

11.03.2016, 11:14

Wie gesagt, schreckliches Beispiel. Mir fiele aber auf Anhieb aber auch nichts besseres ein. Geht nur darum, diverse D Funktionalitäten in C++ nachzuprogrammieren. Jedenfalls so nah wie es halt geht. Das inout im Parameter noch halbwegs zu deduzieren funktioniert ja, aber den Modifier einer Methode ist wohl unmöglich.

Viele störte das doppelte Implementieren von z.B. Zugriffsoperatoren in C++ (das wäre glaub auch ein wesentlich besseres Beispiel gewesen):

C-/C++-Quelltext

1
2
3
4
5
6
7
T& operator[](std::size_t index) {
    return ...;
}

const T& operator[](std::size_t index) const {
    return ...;
}


Das geht in D in einer Methode dank inout:

Quellcode

1
2
3
inout(T) opIndex(size_t index) inout {
    return ...;
}
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

8

11.03.2016, 11:19

Naja, du kannst die Methode, solange sie nicht virtual sein muss, als friend function implementieren, dann kannst du dort das const deducen. Den cv Qualifier einer Methode offen zu lassen geht leider net, da müsste man sich dann für den konkreten Fall was überlegen...

Architekt

Community-Fossil

  • »Architekt« ist der Autor dieses Themas

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

9

11.03.2016, 11:22

Naja, du kannst die Methode, solange sie nicht virtual sein muss, als friend function implementieren, dann kannst du dort das const deducen.

Wie sähe das aus? Als wie würde dort const deduced werden?
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

10

14.03.2016, 11:20

Sorry, hab ganz vergessen meine Antwort hier fertig zu schreiben...

Viele störte das doppelte Implementieren von z.B. Zugriffsoperatoren in C++ (das wäre glaub auch ein wesentlich besseres Beispiel gewesen):

C-/C++-Quelltext

1
2
3
4
5
6
7
T& operator[](std::size_t index) {
    return ...;
}

const T& operator[](std::size_t index) const {
    return ...;
}

Nun, eine Lösung, die in diesem konkreten Fall in der Regel anwendbar ist, ist der bekannte Trick nach Scott Meyers die non-const Variante über die const Variante zu implementieren:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
const T& Bla::operator[](std::size_t index) const
{
    return ...;
}

T& Bla::operator[](std::size_t index)
{
    return const_cast<T&>(static_cast<const Bla&>(*this)[index]);
}




Naja, du kannst die Methode, solange sie nicht virtual sein muss, als friend function implementieren, dann kannst du dort das const deducen.

Wie sähe das aus? Als wie würde dort const deduced werden?

Sorry, da war ich noch etwas verwirrt von deinem Beispiel und dachte, dass nicht das const des Bla, sondern das const des Visitor deduced werden soll; das ginge z.B. einfach so:

C-/C++-Quelltext

1
2
3
4
5
  template <typename Visitor>
  friend void accept(std::conditional_t<std::is_const<Visitor>::value, const Bla, Bla>& bla, Visitor& v)
  {
    v.visit(bla);
  }


Für anders herum kann man wohl mit SFINAE was bauen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <typename T, typename B>
struct inout : std::false_type {};

template <typename T>
struct inout<const T, T> : std::true_type {};

template <typename T>
struct inout<T, T> : std::true_type {};

template <typename T, typename B>
constexpr bool inout_v = inout<T, B>::value;


class Bla
{
public:  
  template <typename T>
  friend std::enable_if_t<inout_v<T, Bla>> accept(T& bla, Visitor& v)
  {
    v.visit(bla);
  }
};


So kann man sich so wohl sogar eine recht elegante Version dieses inout Schlüsselwortes in C++ selbst basteln. Und wenn wir in C++17 dann hoffentlich Unified Call Syntax haben...

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »dot« (14.03.2016, 11:36)


Werbeanzeige