Was sind einige vernünftige stilistische Grenzen für die Typinferenz?

8

C ++ 0x bietet eine verdammt umfassende Unterstützung für Typinferenzen. Ich bin sehr versucht, es überall zu verwenden, um unnötige Wiederholungen zu vermeiden, aber ich frage mich, ob das Entfernen expliziter Typinformationen überall eine so gute Idee ist. Betrachten Sie dieses ziemlich erfundene Beispiel:

Foo.h::

#include <set>

class Foo {
private:

    static std::set<Foo*> instances;

public:

    Foo();
    ~Foo();

    // What does it return? Who cares! Just forward it!
    static decltype(instances.begin()) begin() {
        return instances.begin();
    }

    static decltype(instances.end()) end() {
        return instances.end();
    }

};

Foo.cpp::

#include <Foo.h>
#include <Bar.h>

// The type need only be specified in one location!
// But I do have to open the header to find out what it actually is.
decltype(Foo::instances) Foo::instances;

Foo() {

    // What is the type of x?
    auto x = Bar::get_something();

    // What does do_something() return?
    auto y = x.do_something(*this);

    // Well, it's convertible to bool somehow...
    if (!y) throw "a constant, old school";

    instances.insert(this);

}

~Foo() {
    instances.erase(this);
}

Würden Sie sagen, dass dies vernünftig ist oder ist es völlig lächerlich? Schließlich, vor allem , wenn Sie es gewohnt sind in einer dynamischen Sprache zu entwickeln, müssen Sie nicht wirklich brauchen vertrauen , dass alle viel über die Arten der Dinge, und kann zu sorgen , dass der Compiler keine ungeheuerlichen Missbrauch des Typsystems fangen. Aber für diejenigen unter Ihnen, die auf die Unterstützung von Editoren für Methodensignaturen angewiesen sind, haben Sie kein Glück. Daher ist die Verwendung dieses Stils in einer Bibliotheksschnittstelle wahrscheinlich eine schlechte Praxis.

Ich finde, dass das Schreiben von Dingen mit allen möglichen impliziten Typen es mir tatsächlich viel einfacher macht, meinem Code zu folgen, da dadurch fast die gesamte übliche Unordnung von C ++ beseitigt wird. Ihr Kilometerstand kann natürlich variieren, und das ist es, worüber ich gerne hören möchte. Was sind die spezifischen Vor- und Nachteile einer radikalen Verwendung der Typinferenz?

Jon Purdy
quelle
Eigentlich Sie tun müssen , um kümmern sich um Arten , wenn Sie in einer dynamischen Sprache sind zu entwickeln; Wenn etwas nicht stimmt, finden Sie es erst heraus, wenn Sie diesen Codeabschnitt zur Laufzeit erreicht haben.
Larry Coleman
@ Larry: Natürlich. Aber nicht in dem Sinne, dass Sie den genauen Typ und die Vererbungskette jedes verwendeten Objekts vorhersagen müssen.

Antworten:

5

Da ich selbst hauptsächlich Python-Programmierer bin, teile ich die Einstellung, dass der Programmierer den genauen Typ nicht kennen muss. Im Fall von C ++, insbesondere beim Umgang mit Vorlagen Vorlagen Vorlagen ... Vorlagen. Das liegt natürlich nicht daran, dass ich statisches Tippen ablehne (ich nicht - ich halte Haskell für eines der besten Dinge seit geschnittenem Brot, teilweise wegen seiner statischen Typisierung), sondern daran, dass mir der genaue Typ nicht wichtig ist. Warum, wird nicht gut durch Beispiele demonstriert, die Namen wie foooder verwenden get_stuff(). Wählen wir also etwas, das der Realität näher kommt:

auto users = get_users();
vector<decltype(users[0])> blocked_users;
/* I'm not a C++ guru, much less C++0x so forgive me if type
   inference and foreach must be combined differently */
for (auto user : users) {
    if (check_name(user.name)) blocked_users.push_back(user)
}

Keine einzige Typanmerkung, aber es ist völlig klar, was sie tut, oder? Diesem Code ist es egal, um welche Art es sich handelt users, er benötigt nur einen Bereich, um zu iterieren, der Dinge mit einem enthält name, dem zugeführt werden kann check_name. Nichts mehr. Niemand kümmert sich um die 100-stelligen Zeichen von Typnamen, die Sie sonst eingeben müssten.

Gleiches gilt für die meisten Codes mit aussagekräftigen Namen. Das Beispiel in der Frage ist nicht klar, aber es wäre auch nicht klar mit expliziten Typnamen, da weder Kontext noch Bezeichner einen Hinweis darauf geben, was los ist. Verwenden Sie aussagekräftige Bezeichner, und der Code kann unabhängig von expliziten Typanmerkungen verstanden werden.


quelle
3

Der Grund, warum sie Schlussfolgerungen gezogen haben, war zu verhindern, dass Leute Code schreiben wie:

foo().bar().baz().etc();

Wann könnten Sie schreiben:

Foo f = foo();
Bar b = f.bar();
...

Außer, dass Foo ein Typ mit langen Vorlagen wäre, also wird es schöner zu schreiben:

auto f = foo();
auto b = f.bar();
...

Als Faustregel gilt: Wenn Sie bei Verwendung von auto Code wie oben anstelle von Code ähnlich dem ersten Beispiel schreiben, verwenden Sie ihn auf jeden Fall. Andernfalls bleiben Sie beim Hinzufügen expliziter Definitionen.

dan_waterworth
quelle
Macht Sinn. Ich finde mich autoin vielen Fällen wieder, in denen ich sicher bin, dass andere explizit sind, aber ich sehe nicht, was wirklich falsch ist, sagen wir auto f = new SomethingLong(), weil es offensichtlich ist, was der Ausdruck zurückgibt. Ich frage mich nur, wo ich die Grenze ziehen soll.
Jon Purdy
Das ist in Ordnung, wenn SomethingLonges nicht Teil einer Vererbungsstruktur ist, aber wenn Sie nicht sicher sind, dass dies niemals der Fall sein wird, würde ich davon abraten. Es ist viel einfacher, die Linie auf der vorsichtigen Seite zu ziehen.
dan_waterworth
2

Es gibt gute Gründe, die Typinferenz nicht immer zu verwenden. Haskell hat Typinferenz, aber normalerweise deklariere ich Funktionstypen trotzdem explizit. Das ist teilweise ein Ergebnis meines Entwicklungsstils. Ich deklariere zuerst die Funktion:

myFunction :: [Int] -> Int
myFunction xs = undefined
Als nächstes schreibe ich den Code, der diese Funktion verwendet, und kompiliere, um sicherzustellen, dass die Typprüfungen durchgeführt werden. Sobald die Typprüfungen abgeschlossen sind, werde ich mit der Implementierung fortfahren.

Der andere Grund für die Deklaration von Funktionstypen besteht darin, dass die Deklarationen als zusätzliche Dokumentation dienen können. Dass diese Dokumentation bei jeder Kompilierung überprüft wird, ist ein Bonus.

Larry Coleman
quelle
2
Während der Typ-First-Ansatz in Haskell sehr elegant ist, bezweifle ich, dass er für C ++ geeignet ist. Ohne Konzepte , sagt eine C ++ Template - Signatur im Grunde nichts - es ist die Implementierung , die die Anforderungen Typen und Argumente müssen treffen definiert. Vorlagen tippen nur zur Kompilierungszeit - "versuchen Sie, ob es funktioniert". Daher würde ich sagen, wir können mit den Typen genauso implizit umgehen wie dynamische Sprachen mit Ententyp.
Dario