Warum nicht C++ - Unterstützung stark typisierte Auslassungszeichen?
Kann mir jemand bitte erklären, warum C++, zumindest meines Wissens nach gar nicht implementiert, einer stark typisierten Auslassungszeichen Funktion, etwas zum Effekt von:
void foo(double ...) {
//Do Something
}
Bedeutung, dass, im Klartext zu sprechen: 'kann Der Benutzer übergeben Sie eine variable Anzahl von Bedingungen zu der foo-Funktion, aber alle Bedingungen müssen verdoppelt werden'
- Ich würde vermuten, dass variadischen Funktionen wurden Hinzugefügt, um C mit dem alleinigen Zweck der Unterstützung der printf-Familie von Funktionen, die müssen Typ-unsicher. Die format-string-I/O-Konzept selbst wurde wahrscheinlich nur genommen von der C ' s Vorgänger wie BCPL (siehe en.wikipedia.org/wiki/BCPL). In modernem C++, es gibt keine Notwendigkeit, type-safe variadischen Funktionen, denn wir haben superior-Konstrukte sowieso, vor allem seit C++11. Leider habe ich keine Referenzen für meine Vermutungen. Es wäre interessant, diese Frage zu stellen, mit Bjarne Stroustrup selbst.
- Sie tun kann
void foo(double *)
und nennen es durchfoo((double[]){1,2,3,4,5})
. Benötigen GNU C++ - Erweiterung. - Ist nicht diese Funktion zu anekdotisch zu werden, lohnt sich die Einbindung in ein bereits overfeatured Sprache ? Dann sollte man so gut behaupten
void foo(double ..., int ..., double ...)
und dergleichen. - Es gibt keinen fundamentalen Grund, warum der printf-Familie müssen Typ-unsicher. C könnte haben erklärt, dass die Umsetzung zunächst schiebt sich eine "type-token" auf dem call-stack, so dass die vararg-Mechanismus können überprüfen, dass die richtige Art von Wert wird auf den stack. Das würde verlangsamt haben, richtigen code, und C die in der Vergangenheit eine starke Bevorzugung von schnell oben sicher.
- Wahr.
- OTOH, dass immer noch nicht machen es sicher zur compile-Zeit.
- Tatsächlich, ich denke, das wäre ziemlich nützlich...
- Ich denke, dass gcc druckt eine Warnung, wenn die printf/scanf-argument die nicht zu den Formatbezeichner, ich bin sicher, ich habe es gesehen.
- Ich schaffte es Programmieren in C und C++ seit 25 Jahren, ohne jemals das Gefühl der Notwendigkeit für eine Variable Funktion.
- ja, gut, die Bequemlichkeit der funktionalen Sprachen....
- In der Tat. Es funktioniert nur, wenn der string eine Konstante, wenn. Das ist genau, warum printf als angegeben können nicht sicher sein, zur compile-Zeit.
- Es zeigt, wie wenig Pragmatismus könnte eine große Hilfe gewesen, obwohl - diese Warnung hätte incorporated in den K&R C leicht (es ist nicht viel von einer technischen Herausforderung), und würde abgedeckt haben, 99,99% der bugs. Stattdessen wollte Sie werden Puristen und nicht zulassen, dass eine Besondere Behandlung für printf/scanf.
template<class T> using id = T; void foo(double*); foo(id<double[]>{1,2,3,4});
funktioniert w/o-Erweiterungen.- Eine Typ-sichere version könnte wohl gewesen sein, als Billig und in vielen Fällen billiger als der hack, der implementiert wurde, wenn die drucken-Funktion nahm einen compiler-generierten
const char*
Beschreibung der Parameter und der Compiler könnte wählen Sie die vorbeifahrenden Mittel als angemessen, soint a; long b; print(1, 0x12345, a, b, a+b)
einen compiler erzeugen könnte einen temp-holdinga+b
, und übergeben Sie einstatic const char[]
enthält, codiert als integer-Wert 1, der integer-Wert 0x12345, die statische oder die frame-relative Adresse ein, die statische oder die frame-relative Adresse von b, und die frame-relative... - ...- Adresse des compiler-temp-Wert. In den meisten Systemen in die frühen Tage von C, schieben Sie ein zwei-byte-integer-Wert in einer Variablen gespeichert dauern würde 4 bytes von code und schob eine lange, oft doppelte, aber eine relativ einfache Codierung-Methode könnte den Aufwand zu verringern, drücken eines
int
auf zwei bytes (oder vielleicht sogar eins), wenn die variable wurde in der Nähe der Spitze des stack-Rahmens an, als würde oft der Fall ist.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Historisch gesehen, sind die Ellipse-syntax
...
kommt von C.Diese komplizierte Bestie, die verwendet wurde, um die macht
printf
-ähnliche Funktionen und ist mitva_list
,va_start
etc...Wie Sie angemerkt haben, ist es nicht typesafe; dann aber C ist bei weitem nicht typesafe, was mit Ihrer impliziten Konvertierungen von und zu
void*
für alle Zeigertypen, seine implizite Kürzung der Integrale/floating-point-Werte, etc...Weil C++ war es, so nah wie möglich als eine Obermenge von C, es erbte die Auslassung von C.
Seit seiner Gründung, C++ Praktiken entwickelt, und es hat einen starken Schub in Richtung einer stärkeren Typisierung.
In C++11, das gipfelte darin:
foo({1, 2, 3, 4, 5})
printf
zum BeispielVariadic templates eigentlich Wiederverwendung der Ellipse
...
in Ihrer syntax, zu bezeichnen packs von Typen oder Werte und als Auspacken Betreiber:Beachten Sie die 3 verschiedenen Verwendungen von
...
:typename...
deklarieren Sie eine Variable TypArgs const&...
zu erklären, eine Packung Argumenteargs...
zu packen Sie das pack in einem Ausdruckprint
macht nicht viel Sinn. Wo kommen dieargs
gehen ?print
mit 4 Argumenten, die Anrufeprint
mit 3 Argumenten, die Anrufeprint
mit 2 Argumenten, welche Anrufeprint
mit 1 argument => dies ist der base case (non-Variable Funktion) und die Rekursion Stoppt.print
. Macht jetzt mehr Sinn :pEs ist
ganz in der Nähe, dass.
Könnte man auch verwenden, variadic templates, sondern es wird mehr diskursiv. Als für den eigentlichen Grund würde ich sagen, die Mühe zu bringen, die neue syntax ist nicht wahrscheinlich lohnt sich: wie Sie den Zugriff auf die einzelnen Elemente? Wie wissen Sie, Wann zu stoppen? Was macht es besser als, sagen wir,
std::initializer_list
?C++ hat auch noch einmal etwas näher an: non-type-parameter packs.
wie in
aber die Art des nicht-Typ template-parameter (uhm) hat einige Einschränkungen: es kann nicht sein
double
zum Beispiel.Ist es bereits möglich, mit variadic templates und SFINAE :
Dank Columbo für die nette
all_true
trick. Sie werden auch in der Lage, einen Fach-Ausdruck in C++17.Als später und anstehende standards sind, konzentrieren sich auf terser syntax (knappe for-Schleifen, implizite Funktion, Vorlagen...), ist es sehr möglich, dass deine vorgeschlagene syntax landet in der Standard-Tag 😉
error C2783: 'void foo(Doubles...)': could not deduce template argument for '<unnamed-symbol>'
Dafür, warum speziell so eine Sache war nicht vorgeschlagen (oder war vorgeschlagen und abgelehnt), ich weiß nicht. So ein Ding wäre sicherlich nützlich, aber möchte hinzufügen, mehr Komplexität in die Sprache. Als Quentin zeigt, es ist bereits vorgeschlagen, eine C++11 so erreichen so eine Sache mit den Vorlagen.
Wenn Konzepte Hinzugefügt wird, der standard, wir haben ein anderes, mehr prägnante Weise:
oder
oder, wie @dyp Punkte aus:
Persönlich, zwischen der aktuellen Lösung und die, dass wir uns mit Konzepten, habe ich das Gefühl, dass es eine adäquate Lösung für das problem. Vor allem, da die Letzte ist im Grunde das, was man ursprünglich gebeten, für Sie sowieso.
void foo(Convertible<double>... doubles);
(die musste ich nachschlagen, aber ist garantiert zu arbeiten, indem Sie N4377; der Grund ist wohl, dass jede Funktion mit einem Platzhalter im parameter-decl-Klausel ist äquivalent / definiert als eine Funktion Vorlage)AllConvertibleToDouble{...T} void foo(T... t);
- ich bekomme einfach nicht den gcc zu akzeptierenAllConvertible<double>{...T} void foo(T... t);
leider 🙁 -- hmm jetzt will ich definieren, ein Konzept namensTemplate
{
.AllConvertible<double>
ist eine Teil-Konzept-id.Convertible
wird wahrscheinlich genannt werdenConvertibleTo
im Einklang mit der LWG Führung in den Juli Reicht TS Telekonferenzen. (Es sei denn, natürlich, der Ausschuss ändert den Namen wieder 😉qualified-concept-name := nested-name-specifier_opt concept-name
, das ist sehr viel falsch. In jedem Fall, das Konzept der Auflösung Regeln in [temp.KONSTR.lösen] nicht korrekt angeben, wie die form das Konzept argument-Liste für eine partial-Konzept-id im template Einführung. Ich habe eingereicht Ausgabe #92 gegen die Konzepte TS, um es zu beheben.Den Weg zu erreichen ist (eine Art), was Sie vorschlagen, ist die Verwendung von variadic templates
jedoch können Sie weitergeben, geben Sie in der parameter-pack jetzt.
Was Sie vorschlagen, ist nie umgesetzt worden, vielleicht könnte es eine gute Ergänzung für die Sprache, oder es könnte nur sein, zu schwierig zu implementieren, wie die Dinge stehen. Sie könnten immer versuchen, schreiben Sie einen Vorschlag und legt ihn dem isocpp.org
std::initializer_list<double>
nicht tun, und wahrscheinlich (B) erläutern Sie, wiesizeof...
Werke fürdouble...
. Bonus-Punkte für (C) die tatsächlichen vorgeschlagenen Wortlaut für den Standard.Basierend auf Matthew ' s Antwort:
Wenn Sie den code mit
foo
kompiliert, Sie kennen alle die Argumente Cabrio zudouble
.Weil Sie verwenden können,
The user can pass a variable number of terms to the foo function, however, all of the terms must be of type T
foo({0.4, 0.3, 0.2});
]( ideone.com/FduhRH)foo(0.4, 0.3, 0.2);
. Aber Gefühle sind irrational, sowieso 🙂