C ++ 11 stellen benutzerdefinierte Literale , die die Einführung neuer Literalsyntax basierend auf bestehende Literale ermöglichen ( int
, hex
, string
, float
) , so daß jeder Typ in der Lage , eine wörtliche Präsentation zu haben.
Beispiele:
// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{
return std::complex<long double>(0, d);
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)
// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42
// std::string
std::string operator "" _s(const char* str, size_t /*length*/)
{
return std::string(str);
}
auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer
// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds
Auf den ersten Blick sieht das sehr cool aus, aber ich frage mich, wie zutreffend es wirklich ist, als ich versuchte, über die Suffixe nachzudenken _AD
und _BC
Daten zu erstellen, stellte ich fest, dass dies aufgrund der Reihenfolge der Bediener problematisch ist. 1974/01/06_AD
würde zuerst bewerten 1974/01
(als einfache int
s) und erst später die 06_AD
(ganz zu schweigen von August und September, die 0
aus oktalen Gründen ohne die geschrieben werden müssen). Dies kann umgangen werden 1974-1/6_AD
, indem die Syntax so festgelegt wird, dass die Reihenfolge der Operatorauswertung funktioniert, diese jedoch klobig ist.
Meine Frage lautet also: Glauben Sie, dass sich diese Funktion rechtfertigen wird? Welche anderen Literale möchten Sie definieren, um Ihren C ++ - Code besser lesbar zu machen?
Die Syntax wurde an den endgültigen Entwurf vom Juni 2011 angepasst
quelle
string operator "" _s(const char*s);"
kann nicht zum Parsen verwendet werden"hello"_s"
. Dies ist ein Zeichenfolgenliteral und sucht den Operator mit einem zusätzlichensize_t
Parameter. Habe ich recht?uint16_t
deren Verhalten abhängig von der Implementierung, mit ähnlichen Artenuwrap16
undunum16
deren Verhalten wäre Implementierung unabhängig, so dass angesichtsuwrap16 w=1; unum16 n=1;
der Ausdrückew-2
undn-2
würde ergeben(uwrap16)65535
und(int)-1
jeweils [uint16_t
würde das erste Ergebnis auf Systemen liefern, bei denenint
16 Bits ist, und die zweite auf Systeme , bei denenint
größer]. Das größte Problem, das ich sah, war der Umgang mit numerischen Literalen.sizeof
Implementierung abhängige Ganzzahltypen zurückgegeben werden. Die Situation könnte jedoch noch viel besser sein als sie ist. Was halten Sie von diesem Konzept?Antworten:
Hier ist ein Fall, in dem die Verwendung benutzerdefinierter Literale anstelle eines Konstruktoraufrufs von Vorteil ist:
Der Vorteil ist, dass eine Laufzeitausnahme in einen Kompilierungsfehler konvertiert wird. Sie konnten die statische Zusicherung nicht zum Bitset-Ctor hinzufügen, der eine Zeichenfolge verwendet (zumindest nicht ohne Argumente für Zeichenfolgenvorlagen).
quelle
Auf den ersten Blick scheint es sich um einfachen syntaktischen Zucker zu handeln.
Bei genauerer Betrachtung sehen wir jedoch, dass dies mehr als nur syntaktischer Zucker ist, da es die Optionen des C ++ - Benutzers erweitert, um benutzerdefinierte Typen zu erstellen, die sich genau wie verschiedene integrierte Typen verhalten.In dieser Hinsicht ist dieser kleine "Bonus" eine sehr interessante C ++ 11-Ergänzung zu C ++.
Brauchen wir es wirklich in C ++?
Ich sehe nur wenige Verwendungen in dem Code, den ich in den letzten Jahren geschrieben habe, aber nur weil ich ihn nicht in C ++ verwendet habe, heißt das nicht, dass er für einen anderen C ++ - Entwickler nicht interessant ist .
Wir hatten in C ++ (und in C, denke ich) vom Compiler definierte Literale verwendet, um Ganzzahlen als kurze oder lange Ganzzahlen, reelle Zahlen als float oder double (oder sogar long double) und Zeichenfolgen als normale oder breite Zeichen einzugeben .
In C ++ hatten wir die Möglichkeit, eigene Typen (dh Klassen) zu erstellen , möglicherweise ohne Overhead (Inlining usw.). Wir hatten die Möglichkeit, Operatoren zu ihren Typen hinzuzufügen, damit sie sich wie ähnliche integrierte Typen verhalten, wodurch C ++ - Entwickler Matrizen und komplexe Zahlen so natürlich verwenden können, wie dies der Fall wäre, wenn diese der Sprache selbst hinzugefügt worden wären. Wir können sogar Cast-Operatoren hinzufügen (was normalerweise eine schlechte Idee ist, aber manchmal ist es genau die richtige Lösung).
Wir haben immer noch eine Sache übersehen, bei der sich Benutzertypen als integrierte Typen verhalten: benutzerdefinierte Literale.
Ich denke, es ist eine natürliche Entwicklung für die Sprache, aber um so vollständig wie möglich zu sein: " Wenn Sie einen Typ erstellen möchten und möchten, dass er sich so gut wie möglich wie ein eingebauter Typ verhält, sind hier die Werkzeuge. .. "
Ich würde vermuten, dass es der Entscheidung von .NET sehr ähnlich ist, jedes Grundelement zu einer Struktur zu machen, einschließlich Boolescher Werte, Ganzzahlen usw., und alle Strukturen von Object abzuleiten. Diese Entscheidung allein macht .NET bei der Arbeit mit Grundelementen für Java weit unerreichbar, unabhängig davon, wie viel Boxing / Unboxing-Hacks Java zu seiner Spezifikation hinzufügt.
Brauchen SIE es wirklich in C ++?
Diese Frage müssen SIE beantworten. Nicht Bjarne Stroustrup. Nicht Herb Sutter. Nicht irgendein Mitglied des C ++ - Standardkomitees. Aus diesem Grund haben Sie in C ++ die Wahl , und eine nützliche Notation wird nicht nur auf integrierte Typen beschränkt.
Wenn Sie es brauchen, dann ist es eine willkommene Ergänzung. Wenn Sie dies nicht tun, verwenden Sie es nicht. Es wird dich nichts kosten.
Willkommen bei C ++, der Sprache, in der Funktionen optional sind.
Aufgebläht??? Zeig mir deine Komplexe !!!
Es gibt einen Unterschied zwischen aufgebläht und komplex (Wortspiel beabsichtigt).
Wie von Niels unter gezeigt. Welche neuen Funktionen fügen benutzerdefinierte Literale C ++ hinzu? Die Fähigkeit, eine komplexe Zahl zu schreiben, ist eine der beiden Funktionen, die C und C ++ "kürzlich" hinzugefügt wurden:
Jetzt können sowohl der Typ C99 "double complex" als auch der Typ C ++ "std :: complex" mithilfe der Operatorüberladung multipliziert, addiert, subtrahiert usw. werden.
In C99 wurde jedoch nur ein weiterer Typ als integrierter Typ und eine integrierte Unterstützung für das Überladen von Operatoren hinzugefügt. Und sie haben eine weitere integrierte wörtliche Funktion hinzugefügt.
In C ++ verwendeten sie nur vorhandene Funktionen der Sprache, erkannten, dass die wörtliche Funktion eine natürliche Weiterentwicklung der Sprache war, und fügten sie daher hinzu.
Wenn Sie in C dieselbe Notationsverbesserung für einen anderen Typ benötigen, haben Sie bis zu Ihrer Lobbyarbeit kein Glück, Ihre Quantenwellenfunktionen (oder 3D-Punkte oder einen anderen Basistyp, den Sie in Ihrem Arbeitsbereich verwenden) zum hinzuzufügen C-Standard als eingebauter Typ ist erfolgreich.
In C ++ 11 können Sie dies einfach selbst tun:
Ist es aufgebläht? Nein , die Notwendigkeit besteht darin, wie sowohl C- als auch C ++ - Komplexe eine Möglichkeit benötigen, ihre wörtlichen komplexen Werte darzustellen.
Ist es falsch gestaltet? Nein , es wurde wie jede andere C ++ - Funktion entwickelt, wobei die Erweiterbarkeit berücksichtigt wurde.
Ist es nur für Notationszwecke? Nein , da dies Ihrem Code sogar Typensicherheit verleihen kann.
Stellen wir uns zum Beispiel einen CSS-orientierten Code vor:
Es ist dann sehr einfach, eine starke Typisierung für die Zuweisung von Werten zu erzwingen.
Ist das gefährlich?
Gute Frage. Können diese Funktionen mit einem Namespace versehen werden? Wenn ja, dann Jackpot!
Wie alles können Sie sich auch umbringen, wenn ein Werkzeug nicht ordnungsgemäß verwendet wird . C ist mächtig und Sie können sich den Kopf abschießen, wenn Sie die C-Waffe missbrauchen. C ++ hat die C-Pistole, aber auch das Skalpell, den Taser und jedes andere Werkzeug, das Sie im Toolkit finden. Sie können das Skalpell missbrauchen und sich verbluten. Oder Sie können sehr eleganten und robusten Code erstellen.
Brauchen Sie es also wie jedes C ++ - Feature wirklich? Diese Frage müssen Sie beantworten, bevor Sie sie in C ++ verwenden können. Wenn Sie dies nicht tun, kostet es Sie nichts. Aber wenn Sie es zumindest wirklich brauchen, wird Sie die Sprache nicht im Stich lassen.
Das Datumsbeispiel?
Ihr Fehler scheint mir zu sein, dass Sie Operatoren mischen:
Dies kann nicht vermieden werden, da der Compiler als Operator diesen interpretieren muss. Und AFAIK, es ist eine gute Sache.
Um eine Lösung für Ihr Problem zu finden, würde ich das Literal auf andere Weise schreiben. Beispielsweise:
Persönlich würde ich die Ganzzahl und die ISO-Daten wählen, aber es hängt von IHREN Bedürfnissen ab. Das ist der springende Punkt, um den Benutzer seine eigenen Literalnamen definieren zu lassen.
quelle
you can write 1+2i, but you still can't write a+bi, so there's absolutely no point
Selbst wenn Sie Ihra+bi
Beispiel ignorieren, ist das lächerlich. Die Tatsache, dass Sie es als "niedrige Frequenz" wahrnehmen, bedeutet nicht, dass es jeder tut. . . Mit Blick auf das Gesamtbild geht es darum, sicherzustellen, dass benutzerdefinierte Objekte ebenso wie eingebaute Typen so weit wie möglich als erstklassige Bürger der Sprache betrachtet werden können. Also, wenn Sie schreiben können1.5f
und1000UL
warum konnten Sie nicht schreiben25i
oder sogar100101b
? Im Gegensatz zu C und Java gelten Benutzertypen nicht als Bürger zweiter Klasse der Sprache in C ++.Most of data still comes from IO
Es gibt viele fest codierte Werte im Code. Sehen Sie alle booleans, alle Zahlen, alle die Doppel , die in dem Code kommen, weil es bequemer ist , zu schreibenx = 2 * y ;
statt ,x = Two * y
woTwo
eine stark typisierte Konstante. Mit den benutzerdefinierten Literalen können wir einen Typ darauf setzen und schreiben:x = 2_speed * y ;
und den Compiler überprüfen lassen, ob die Berechnung sinnvoll ist. . . Es kommt alles auf starkes Tippen an. . . Vielleicht wirst du es nicht benutzen. Aber ich werde es sicher tun, sobald ich einen C ++ 11-fähigen Compiler bei der Arbeit verwenden kann.Es ist sehr schön für mathematischen Code. Aus meinem Kopf kann ich die Verwendung für die folgenden Operatoren sehen:
Grad für Grad. Das macht das Schreiben von absoluten Winkeln viel intuitiver.
Es kann auch für verschiedene Festpunktdarstellungen verwendet werden (die im Bereich DSP und Grafik noch verwendet werden).
Diese sehen aus wie schöne Beispiele, wie man es benutzt. Sie helfen dabei, Konstanten im Code besser lesbar zu machen. Es ist ein weiteres Tool, um Code ebenfalls unlesbar zu machen, aber wir haben bereits so viele Tools missbraucht, dass ein weiteres nicht viel schadet.
quelle
UDLs haben einen Namespace (und können mithilfe von Deklarationen / Direktiven importiert werden, aber Sie können einen Literal-ähnlichen Namen nicht explizit benennen
3.14std::i
), was bedeutet, dass es (hoffentlich) nicht viele Konflikte gibt.Die Tatsache, dass sie tatsächlich als Vorlage (und als Constexpr'd) verwendet werden können, bedeutet, dass Sie mit UDLs einige ziemlich mächtige Dinge tun können. Bigint-Autoren werden sehr glücklich sein, da sie endlich beliebig große Konstanten haben können, die zur Kompilierungszeit berechnet werden (über constexpr oder Vorlagen).
Ich bin nur traurig, dass wir im Standard (wie es aussieht) nicht ein paar nützliche Literale sehen werden, wie
s
fürstd::string
undi
für die imaginäre Einheit.Die Codierungszeit, die von UDLs eingespart wird, ist tatsächlich nicht so hoch, aber die Lesbarkeit wird erheblich verbessert, und immer mehr Berechnungen können für eine schnellere Ausführung auf die Kompilierungszeit verschoben werden.
quelle
Lassen Sie mich ein wenig Kontext hinzufügen. Für unsere Arbeit werden benutzerdefinierte Literale dringend benötigt. Wir arbeiten an MDE (Model-Driven Engineering). Wir wollen Modelle und Metamodelle in C ++ definieren. Wir haben tatsächlich ein Mapping von Ecore zu C ++ implementiert ( EMF4CPP) implementiert ) .
Das Problem tritt auf, wenn Modellelemente in C ++ als Klassen definiert werden können. Wir verfolgen den Ansatz, das Metamodell (Ecore) in Vorlagen mit Argumenten umzuwandeln. Argumente der Vorlage sind die strukturellen Merkmale von Typen und Klassen. Eine Klasse mit zwei int-Attributen wäre beispielsweise wie folgt:
Es stellt sich jedoch heraus, dass jedes Element in einem Modell oder Metamodell normalerweise einen Namen hat. Wir möchten schreiben:
ABER, C ++ oder C ++ 0x erlauben dies nicht, da Zeichenfolgen als Argumente für Vorlagen verboten sind. Sie können den Namen char für char schreiben, aber das ist zugegebenermaßen ein Durcheinander. Mit richtigen benutzerdefinierten Literalen könnten wir etwas Ähnliches schreiben. Angenommen, wir verwenden "_n", um Modellelementnamen zu identifizieren (ich verwende nicht die genaue Syntax, nur um eine Idee zu machen):
Schließlich hilft es uns sehr, diese Definitionen als Vorlagen zu haben, um Algorithmen zum Durchlaufen der Modellelemente, Modelltransformationen usw. zu entwerfen, die wirklich effizient sind, da Typinformationen, Identifikation, Transformationen usw. vom Compiler zur Kompilierungszeit bestimmt werden.
quelle
by the compiler at compile time
... :-)Bjarne Stroustrup spricht in diesem C ++ 11-Vortrag im ersten Abschnitt über typreiche Schnittstellen über UDLs , etwa 20 Minuten.
Sein grundlegendes Argument für UDLs ist ein Syllogismus:
"Triviale" Typen, dh eingebaute primitive Typen, können nur triviale Typfehler abfangen. Schnittstellen mit umfangreicheren Typen ermöglichen es dem Typsystem, mehr Arten von Fehlern zu erkennen.
Die Arten von Typfehlern, die reich typisierter Code abfangen kann, wirken sich auf echten Code aus. (Er gibt das Beispiel des Mars Climate Orbiter, der aufgrund eines Dimensionsfehlers in einer wichtigen Konstante infamös versagt hat).
Im realen Code werden Einheiten selten verwendet. Die Benutzer verwenden sie nicht, da das Erstellen von Laufzeitberechnungen oder Speicheraufwand zum Erstellen umfangreicher Typen zu kostspielig ist und die Verwendung von bereits vorhandenem C ++ - Vorlagen-Einheitencode so notational hässlich ist, dass niemand ihn verwendet. (Empirisch benutzt es niemand, obwohl es die Bibliotheken schon seit einem Jahrzehnt gibt).
Um Ingenieure dazu zu bringen, Einheiten in echtem Code zu verwenden, brauchten wir daher ein Gerät, das (1) keinen Laufzeitaufwand verursacht und (2) notational akzeptabel ist.
quelle
Die Unterstützung der Dimensionsprüfung zur Kompilierungszeit ist die einzige erforderliche Begründung.
Siehe zum Beispiel PhysUnits-CT-Cpp11 , eine kleine C ++ 11-, C ++ 14-Bibliothek nur für Header zur Dimensionsanalyse zur Kompilierungszeit sowie zur Manipulation und Konvertierung von Einheiten / Mengen. Einfacher als Boost.Units , unterstützt Einheitensymbolliterale wie m, g, s, metrische Präfixe wie m, k, M, hängt nur von der Standard-C ++ - Bibliothek ab, nur SI, integrale Potenzen von Dimensionen.
quelle
Hmm ... Ich habe noch nicht über diese Funktion nachgedacht. Ihre Probe war gut durchdacht und ist sicherlich interessant. C ++ ist so wie es jetzt sehr leistungsfähig ist, aber leider ist die Syntax, die in von Ihnen gelesenen Codeteilen verwendet wird, manchmal zu komplex. Lesbarkeit ist, wenn nicht alles, dann zumindest viel. Und eine solche Funktion wäre auf mehr Lesbarkeit ausgerichtet. Wenn ich Ihr letztes Beispiel nehme
... Ich frage mich, wie Sie das heute ausdrücken würden. Sie hätten eine KG- und eine LB-Klasse und würden implizite Objekte vergleichen:
Und das würde auch reichen. Bei Typen mit längeren Namen oder Typen, von denen Sie nicht hoffen, dass sie einen so netten Konstruktor zum Schreiben eines Adapters haben, ist dies möglicherweise eine nette Ergänzung für die spontane implizite Objekterstellung und -initialisierung. Andererseits können Sie Objekte auch bereits mit Methoden erstellen und initialisieren.
Aber ich stimme Nils in Bezug auf Mathematik zu. C- und C ++ - Trigonometriefunktionen erfordern beispielsweise die Eingabe im Bogenmaß. Ich denke jedoch in Grad, so dass eine sehr kurze implizite Konvertierung wie Nils sehr schön ist.
Letztendlich wird es jedoch syntaktischer Zucker sein, aber es wird einen leichten Einfluss auf die Lesbarkeit haben. Und es wird wahrscheinlich auch einfacher sein, einige Ausdrücke zu schreiben (sin (180.0deg) ist einfacher zu schreiben als sin (deg (180.0)). Und dann wird es Leute geben, die das Konzept missbrauchen. Aber dann sollten sprachmissbräuchliche Leute verwenden eher sehr restriktive Sprachen als etwas so Ausdrucksvolles wie C ++.
Ah, mein Beitrag sagt im Grunde nichts außer: Es wird in Ordnung sein, die Auswirkungen werden nicht zu groß sein. Machen wir uns keine Sorgen. :-)
quelle
Ich habe diese Funktion nie gebraucht oder wollte (aber dies könnte der Blub- Effekt sein). Meine Knie-Ruck-Reaktion ist, dass es lahm ist und wahrscheinlich dieselben Leute anspricht, die denken, dass es cool ist, Operator + für jede Operation zu überlasten, die aus der Ferne als Hinzufügen ausgelegt werden könnte.
quelle
In C ++ ist die verwendete Syntax normalerweise sehr streng - abgesehen vom Präprozessor können Sie nicht viel zum Definieren einer benutzerdefinierten Syntax / Grammatik verwenden. Zum Beispiel können wir vorhandene Operaten überladen, aber wir können keine neuen definieren - IMO ist dies sehr im Einklang mit dem Geist von C ++.
Ich habe nichts gegen einige Möglichkeiten für individuelleren Quellcode - aber der gewählte Punkt scheint mir sehr isoliert zu sein, was mich am meisten verwirrt.
Selbst die beabsichtigte Verwendung kann das Lesen von Quellcode erheblich erschweren: Ein einzelner Buchstabe kann weitreichende Nebenwirkungen haben, die im Kontext in keiner Weise erkennbar sind. Mit Symmetrie zu u, l und f wählen die meisten Entwickler einzelne Buchstaben.
Dies kann auch das Scoping zu einem Problem machen. Die Verwendung einzelner Buchstaben im globalen Namespace wird wahrscheinlich als schlechte Praxis angesehen, und die Tools, die das Mischen von Bibliotheken erleichtern sollen (Namespaces und beschreibende Bezeichner), werden wahrscheinlich ihren Zweck verfehlen.
Ich sehe einige Vorteile in Kombination mit "Auto", auch in Kombination mit einer Einheitenbibliothek wie Boost-Einheiten , aber nicht genug, um diese Ergänzung zu verdienen.
Ich frage mich jedoch, welche cleveren Ideen wir haben.
quelle
using single letters in global namespace will probably be considered bad practice
Dies hat jedoch keine Relevanz: (A) UDLs müssen im (nicht globalen) Namespace-Bereich definiert werden ... vermutlich, weil (B) sie aus einem Unterstrich bestehen müssen, dann> = 1 Buchstabe, nicht nur dem Buchstaben, und solchen Bezeichnern in Die globalen NS sind für die Implementierung reserviert. Das sind mindestens 2 Punkte gegen die Idee, dass UDLs von Natur aus Verwirrung stiften. Wenn Sie einen Namespace für den Bereich benötigen, der den Nutzen der Funktion verringert, deklariert die stdlib sie beispielsweise ininline namespace
s, damit Benutzer sie bei Bedarf im Großhandel importieren können.Ich habe Benutzerliterale für Binärzeichenfolgen wie diese verwendet:
Verwenden Sie den
std::string(str, n)
Konstruktor, damit\0
die Zeichenfolge nicht halbiert wird. (Das Projekt arbeitet viel mit verschiedenen Dateiformaten.)Dies war auch hilfreich, als ich mich
std::string
für einen Wrapper entschieden habestd::vector
.quelle
Das Leitungsrauschen in diesem Ding ist riesig. Es ist auch schrecklich zu lesen.
Lassen Sie mich wissen, haben sie diese neue Syntaxaddition mit irgendwelchen Beispielen begründet? Haben sie zum Beispiel einige Programme, die bereits C ++ 0x verwenden?
Für mich ist dieser Teil:
Rechtfertigt diesen Teil nicht:
Nicht einmal, wenn Sie die i-Syntax auch in 1000 anderen Zeilen verwenden würden. Wenn Sie schreiben, schreiben Sie wahrscheinlich auch 10000 Zeilen von etwas anderem. Vor allem, wenn Sie wahrscheinlich noch meistens überall schreiben werden:
Das 'Auto'-Schlüsselwort kann jedoch nur vielleicht gerechtfertigt sein. Aber nehmen wir nur C ++, weil es in dieser Hinsicht besser als C ++ 0x ist.
Es ist wie ... so einfach. Auch wenn alle Standard- und spitzen Klammern nur lahm sind, wenn Sie sie überall verwenden. Ich fange nicht an zu erraten, welche Syntax es in C ++ 0x gibt, um std :: complex unter complex zu verwandeln.
Das ist vielleicht etwas Unkompliziertes, aber ich glaube nicht, dass es in C ++ 0x so einfach ist.
Vielleicht? > :)
Auf jeden Fall geht es darum, 3.14i anstelle von std :: complex (0, 3.14) zu schreiben; spart Ihnen insgesamt nicht viel Zeit, außer in wenigen Super-Sonderfällen.
quelle
std::complex<double> val(0, 3.14);
.