Welche neuen Funktionen fügen benutzerdefinierte Literale C ++ hinzu?


139

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 _ADund _BCDaten zu erstellen, stellte ich fest, dass dies aufgrund der Reihenfolge der Bediener problematisch ist. 1974/01/06_ADwürde zuerst bewerten 1974/01(als einfache ints) und erst später die 06_AD(ganz zu schweigen von August und September, die 0aus 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


8
Ich werde dafür stimmen. Der Titel ist ganz klar entzündlich.
Welpe

76
@DeadMG, wenn Sie ein Problem mit dem Titel haben, können Sie es bearbeiten. Es ist ein bisschen lustig zu versuchen, eine 3 Jahre alte Frage zu schließen, die 11 positive Stimmen und 8 Favoriten hat. (Nicht die Erwähnung, dass sich die Etikette auf dieser Website in den letzten 3 Jahren geändert hat).
Motti

5
Ich denke, Sie haben einen Fehler in Ihren Beispielen: 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ätzlichen size_tParameter. Habe ich recht?
Towi

1
Eine Sache , die ich gefragt haben, ob es Sinn machen würde „portable C“ in C ++ zu schreiben, Typen zu ersetzen wie uint16_tderen Verhalten abhängig von der Implementierung, mit ähnlichen Arten uwrap16und unum16deren Verhalten wäre Implementierung unabhängig, so dass angesichts uwrap16 w=1; unum16 n=1;der Ausdrücke w-2und n-2würde ergeben (uwrap16)65535und (int)-1jeweils [ uint16_twürde das erste Ergebnis auf Systemen liefern, bei denenint 16 Bits ist, und die zweite auf Systeme , bei denen intgrößer]. Das größte Problem, das ich sah, war der Umgang mit numerischen Literalen.
Superkatze

1
Die Möglichkeit, dass numerische Literale reibungslos mit anderen numerischen Typen mit definiertem Verhalten zusammenarbeiten, scheint es zu ermöglichen, solche Typen zum Erstellen einer Sprache zu verwenden, in der Code, der implementierungsabhängige Aktionen ausführen möchte, dies tun kann, ohne sich auf die Implementierung verlassen zu müssen. definierte Verhaltensweisen. Es gibt einige Stellen, an denen IDB immer noch unvermeidbar ist, da beispielsweise Zeigerunterschiede und von der sizeofImplementierung abhängige Ganzzahltypen zurückgegeben werden. Die Situation könnte jedoch noch viel besser sein als sie ist. Was halten Sie von diesem Konzept?
Superkatze

Antworten:


71

Hier ist ein Fall, in dem die Verwendung benutzerdefinierter Literale anstelle eines Konstruktoraufrufs von Vorteil ist:

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

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).


7
Sie können das Gleiche tun, indem Sie std :: bitset einen geeigneten constexpr-Konstruktor geben.
Nicol Bolas

1
@NicolBolas Du hast recht. Ich bin tatsächlich überrascht, dass einer nicht da ist. Vielleicht sollten wir ein oder zwei für 2014 vorschlagen, wenn es nicht zu spät ist.
Emsr

192

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:

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

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:

Point p = 25_x + 13_y + 3_z ; // 3D point

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:

css::Font::Size p0 = 12_pt ;       // Ok
css::Font::Size p1 = 50_percent ;  // Ok
css::Font::Size p2 = 15_px ;       // Ok
css::Font::Size p3 = 10_em ;       // Ok
css::Font::Size p4 = 15 ;         // ERROR : Won't compile !

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:

1974/01/06AD
    ^  ^  ^

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:

"1974-01-06"_AD ;   // ISO-like notation
"06/01/1974"_AD ;   // french-date-like notation
"jan 06 1974"_AD ;  // US-date-like notation
19740106_AD ;       // integer-date-like notation

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.


1
Vielen Dank, ich und meine anderen alternativen Persönlichkeiten wurden entdeckt. Im Ernst, ich habe das alleine geschrieben, aber vielleicht verwende ich einige Ausdrücke meiner Muttersprache und sie lassen sich nicht gut ins Englische übersetzen.
Paercebal

Was die "verschiedenen Teile" betrifft, wie ihre Titel zeigen, tut mir leid, ich denke, dies organisiert einen ziemlich langen Beitrag. Der fett gedruckte Text ist die Zusammenfassung des Absatzes, in dem sie sich befinden. Personen, die nur die Informationen ohne Begründung wünschen, können das Lesen der Titel und des fett gedruckten Textes einschränken.
Paercebal

3
+1. Wirklich schöne Erklärung. Wir warten darauf, dass dies umgesetzt wird. Das ist wirklich wichtig für uns. Wir arbeiten an MDE (Model-Driven Engineering) und finden dies eine Notwendigkeit. Ich füge unten eine Antwort hinzu, um unseren Fall zu erklären.
Diego Sevilla

9
@TGV :: you can write 1+2i, but you still can't write a+bi, so there's absolutely no pointSelbst wenn Sie Ihr a+biBeispiel 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önnen 1.5fund 1000ULwarum konnten Sie nicht schreiben 25ioder sogar 100101b? Im Gegensatz zu C und Java gelten Benutzertypen nicht als Bürger zweiter Klasse der Sprache in C ++.
Paercebal

3
@Anton :: Most of data still comes from IOEs 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 schreiben x = 2 * y ;statt , x = Two * ywo Twoeine 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.
Paercebal

36

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.

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

Es kann auch für verschiedene Festpunktdarstellungen verwendet werden (die im Bereich DSP und Grafik noch verwendet werden).

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

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.


1
"Aber wir haben bereits so viele Tools missbraucht, dass eines nicht viel schadet. " Wow, ich hoffe, das ist nicht die Philosophie hinter all dem C ++ [x] 1234567890-Feature-Flooding in letzter Zeit. Stellen Sie sich vor, Sie müssen eine Bibliothek lernen, die all diese verwendet, plus zehn Dateiformate für die Konfiguration und zwei Tools für die Vor- und Nachbearbeitung
Ihres

@masterxilo In Wirklichkeit ist Ihr Beispiel natürlich absurd: UDLs sind nicht schwieriger zu erlernen als Funktionen, da sie nur Syntaxzucker sind - und außerdem verwendet jede gute Bibliothek nur die erforderlichen Funktionen, um UX zu verbessern - und dokumentiert genau das, was sie ist alle Mittel. Wenn jemand eine Funktion überbeansprucht, um unlesbaren Code zu generieren (vorausgesetzt, dies ist in seiner Arbeit überhaupt vermeidbar ...), wird diese Funktion nicht beanstandet, sondern nur die Verwendung. Außerdem ist das Unlesbare einer Person das Brot und die Butter einer anderen Person. Es sind alles Meinungen - und Optionen . Wenn Sie sie nicht mögen, machen Sie sich keine Sorgen! Sie müssen nicht haben , um sie zu verwenden. Andere können .
underscore_d

17

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 sfür std::stringundi 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.


Vielen Dank, dass Sie den Punkt über Namespaces geklärt haben ... das habe ich mich gefragt.
Nathan Reed

12

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:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

Es stellt sich jedoch heraus, dass jedes Element in einem Modell oder Metamodell normalerweise einen Namen hat. Wir möchten schreiben:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

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):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

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.


4
Ich mag den Teil sehr sehrby the compiler at compile time ... :-)
paercebal

12

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:

  1. "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.

  2. 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).

  3. 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).

  4. 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.


9

Die Unterstützung der Dimensionsprüfung zur Kompilierungszeit ist die einzige erforderliche Begründung.

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

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.


Oder sehen Sie Einheiten , eine Bibliothek zur Kompilierungszeit, nur für Header, Dimensionsanalyse und Einheitenkonvertierung, die auf c ++ 14 ohne Abhängigkeiten von Nic Holthaus basiert .
Martin Moene

6

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

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

... 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:

assert(KG(1.0f) == LB(2.2f));

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. :-)


5
Ihre Klammern sind unausgeglichen! Entschuldigung, meine OCD hasst mich auch.
X-Istence

3

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.


Ich bestätige: Sehr interessanter Artikel.
Paercebal

2

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.


1
using single letters in global namespace will probably be considered bad practiceDies 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 in inline namespaces, damit Benutzer sie bei Bedarf im Großhandel importieren können.
underscore_d

2

Ich habe Benutzerliterale für Binärzeichenfolgen wie diese verwendet:

 "asd\0\0\0\1"_b

Verwenden Sie den std::string(str, n)Konstruktor, damit \0die Zeichenfolge nicht halbiert wird. (Das Projekt arbeitet viel mit verschiedenen Dateiformaten.)

Dies war auch hilfreich, als ich mich std::stringfür einen Wrapper entschieden habe std::vector.


-5

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:

auto val = 3.14_i

Rechtfertigt diesen Teil nicht:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

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:

std::complex<double> val = 3.14i

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.

std::complex<double> val = std::complex(0, 3.14);

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.

complex = std::complex<double>;

Das ist vielleicht etwas Unkompliziertes, aber ich glaube nicht, dass es in C ++ 0x so einfach ist.

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

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.


10
@Cheery: Für Sie rechtfertigt "auto val = 3.14i" nicht den Code, der zur Unterstützung geschrieben wurde. Ich könnte darauf antworten, dass "printf ("% i ", 25)" für mich den für printf geschriebenen Code nicht rechtfertigt. Sehen Sie ein Muster?
Paercebal

5
@Cheery: "Linienrauschen in diesem Ding ist riesig". Nein, es ist nicht ... "Auch es ist schrecklich zu lesen". Ihr subjektives Argument ist interessant, aber Sie sollten sich die Überladung von Operatoren im Allgemeinen ansehen, um zu sehen, dass der Code für diese Funktion
alles andere als

3
Auto hilft bei der Lesbarkeit. Erwägen Sie die Verwendung von Interatoren in einer for-Schleife: for (auto it = vec.begin (); it! = vec.end (); ++ it) ... Ich kenne for_each, möchte aber keinen Funktor erstellen, um es zu verwenden .
KitsuneYMG

1
@kts: Mit C ++ 1x haben wir Lambda und Reichweite für
Joe D

3
Wenn Ihre C ++ - Zeile besser ist als C ++ 0x, dann ist meine Zeile noch besser. Schreiben Sie einfach : std::complex<double> val(0, 3.14);.
Ben Voigt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.