Quali nuove funzionalità aggiungono letterali definiti dall'utente al C ++?


139

C ++ 11 introduce letterali definiti dall'utente che permetteranno l'introduzione di nuova sintassi letterale basato su letterali esistenti ( int, hex, string, float) in modo che qualsiasi tipo saranno in grado di avere una presentazione letterale.

Esempi:

// 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

A prima vista sembra molto bello, ma mi chiedo quanto sia realmente applicabile, quando ho provato a pensare di avere i suffissi _ADe _BCcreare le date ho scoperto che è problematico a causa dell'ordine dell'operatore. 1974/01/06_ADvaluterebbe prima 1974/01(come pianura int) e solo più tardi il 06_AD(per non parlare di agosto e settembre che devono essere scritti senza le 0ragioni ottali). Questo può essere risolto facendo in modo che la sintassi sia in 1974-1/6_ADmodo che l'ordine di valutazione dell'operatore funzioni ma è goffo.

Quindi la mia domanda si riduce a questa: credi che questa caratteristica si giustificherà? Quali altri letterali vorresti definire che renderanno più leggibile il tuo codice C ++?


Sintassi aggiornata per adattarsi alla bozza finale di giugno 2011


8
Voterò per chiudere questo. Il titolo è abbastanza chiaramente infiammatorio.
Cucciolo,

76
@DeadMG, se hai un problema con il titolo puoi modificarlo. È un po 'divertente cercare di chiudere una domanda di 3 anni con 11 voti positivi e 8 preferiti. (Non la menzione che l'etichetta su questo sito è cambiata negli ultimi 3 anni).
Motti,

5
Penso che tu abbia un errore nei tuoi esempi: string operator "" _s(const char*s);"non può essere usato per analizzare "hello"_s". Questa è una stringa letterale e cercherà l'operatore con un size_tparametro aggiuntivo . Ho ragione?
towi,

1
Una cosa che mi chiedevo è se avrebbe senso scrivere "C portatile" in C ++, sostituendo tipi come il uint16_tcui comportamento dipende dall'implementazione, con tipi simili uwrap16e il unum16cui comportamento sarebbe indipendente dall'implementazione, tale che dato uwrap16 w=1; unum16 n=1;le espressioni w-2e n-2produrrebbe (uwrap16)65535e (int)-1, rispettivamente [ uint16_tprodurrebbe il primo risultato su sistemi dove intè 16 bit, e il secondo su sistemi dove intè più grande]. Il problema più grande che ho visto è stato la gestione di valori letterali numerici.
supercat

1
Essere in grado di far interagire senza problemi i letterali numerici con altri tipi numerici a comportamento definito sembrerebbe che dovrebbe consentire a tali tipi di essere utilizzati per creare un linguaggio in cui il codice che voleva eseguire azioni dipendenti dall'implementazione potrebbe farlo senza dover fare affidamento sull'implementazione- comportamenti definiti. Ci sono alcuni posti in cui IDB sarà ancora inevitabile perché cose come le differenze di puntatore e sizeofrestituiscono tipi interi dipendenti dall'implementazione, ma la situazione potrebbe ancora essere migliorata molto di quanto non sia. Cosa ne pensi di quel concetto?
supercat

Risposte:


71

Ecco un caso in cui esiste un vantaggio nell'utilizzare valori letterali definiti dall'utente anziché una chiamata del costruttore:

#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");
}

Il vantaggio è che un'eccezione di runtime viene convertita in un errore di compilazione. Non è stato possibile aggiungere l'asserzione statica al ctor del set di bit prendendo una stringa (almeno non senza argomenti del modello di stringa).


7
Puoi fare la stessa cosa dando a std :: bitset un costruttore constexpr appropriato.
Nicol Bolas,

1
@NicolBolas Hai ragione. In realtà sono sorpreso che non ci sia. Forse dovremmo proporne uno o due per il 2014 se non è troppo tardi.
emsr

192

A prima vista, sembra essere semplice zucchero sintattico.

Ma se guardiamo più in profondità, vediamo che è molto più di uno zucchero sintattico, poiché estende le opzioni dell'utente di C ++ per creare tipi definiti dall'utente che si comportano esattamente come tipi predefiniti distinti. In questo, questo piccolo "bonus" è un'aggiunta C ++ 11 molto interessante a C ++.

Ne abbiamo davvero bisogno in C ++?

Vedo pochi usi nel codice che ho scritto negli anni passati, ma solo perché non l'ho usato in C ++ non significa che non sia interessante per un altro sviluppatore C ++ .

Avevamo usato in C ++ (e in C, immagino), valori letterali definiti dal compilatore, per digitare numeri interi come numeri interi corti o lunghi, numeri reali come float o double (o anche double long) e stringhe di caratteri come caratteri normali o ampi .

In C ++, abbiamo avuto la possibilità di creare i nostri tipi (cioè classi), potenzialmente senza costi generali (inline, ecc.). Abbiamo avuto la possibilità di aggiungere operatori ai loro tipi, per farli comportare come tipi integrati simili, il che consente agli sviluppatori C ++ di utilizzare matrici e numeri complessi nel modo più naturale che avrebbero se fossero stati aggiunti al linguaggio stesso. Possiamo anche aggiungere operatori di cast (che di solito è una cattiva idea, ma a volte è la soluzione giusta).

Ci mancava ancora una cosa che i tipi di utenti si comportassero come tipi predefiniti: valori letterali definiti dall'utente.

Quindi, suppongo sia una naturale evoluzione per il linguaggio, ma per essere il più completo possibile: " Se vuoi creare un tipo e vuoi che si comporti il ​​più possibile di un tipo incorporato, ecco gli strumenti. .. "

Immagino che sia molto simile alla decisione di .NET di trasformare ogni primitiva in una struttura, inclusi valori booleani, interi, ecc. E che tutte le strutture derivino da Object. Questa decisione da sola pone .NET molto al di fuori della portata di Java quando si lavora con le primitive, non importa quanti hack di boxe / unboxing Java aggiungerà alle sue specifiche.

Ne hai davvero bisogno in C ++?

A questa domanda devi rispondere. Non Bjarne Stroustrup. Non Herb Sutter. Non qualunque sia il membro del comitato standard C ++. Questo è il motivo per cui hai la scelta in C ++ e non limiteranno una notazione utile ai soli tipi predefiniti.

Se ne hai bisogno, allora è una gradita aggiunta. Se non lo fai, beh ... Non usarlo. Non ti costerà nulla.

Benvenuto in C ++, la lingua in cui le funzionalità sono opzionali.

Gonfio ??? Mostrami i tuoi complessi !!!

C'è una differenza tra gonfio e complesso (gioco di parole intenzionale).

Come mostrato da Niels su Quali nuove funzionalità aggiungono letterali definiti dall'utente al C ++? , poter scrivere un numero complesso è una delle due funzionalità aggiunte "recentemente" a C e C ++:

// 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 ;

Ora, sia il tipo "doppio complesso" C99 che il tipo "std :: complex" C ++ possono essere moltiplicati, aggiunti, sottratti, ecc., Utilizzando il sovraccarico dell'operatore.

Ma in C99, hanno appena aggiunto un altro tipo come tipo incorporato e il supporto di sovraccarico dell'operatore integrato. E hanno aggiunto un'altra caratteristica letterale integrata.

In C ++, hanno appena usato le funzionalità esistenti del linguaggio, hanno visto che l'elemento letterale era una naturale evoluzione del linguaggio e quindi l'hanno aggiunto.

In C, se hai bisogno dello stesso miglioramento della notazione per un altro tipo, sei sfortunato fino a quando fai pressioni per aggiungere le tue funzioni di onda quantistica (o punti 3D o qualsiasi tipo di base che stai usando nel tuo campo di lavoro) al Lo standard C come tipo incorporato ha esito positivo.

In C ++ 11, puoi farlo tu stesso:

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

È gonfio? No , c'è la necessità, come dimostrato da come entrambi i complessi C e C ++ hanno bisogno di un modo per rappresentare i loro valori complessi letterali.

È stato progettato in modo errato? No , è progettato come qualsiasi altra funzionalità C ++, con in mente l'estensibilità.

È solo a scopo di notazione? No , in quanto può persino aggiungere la sicurezza dei tipi al tuo codice.

Ad esempio, immaginiamo un codice orientato CSS:

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 !

È quindi molto semplice applicare una digitazione forte all'assegnazione dei valori.

È pericoloso?

Buona domanda. Queste funzioni possono avere lo spazio dei nomi? Se sì, allora Jackpot!

Ad ogni modo, come tutto, puoi ucciderti se uno strumento viene usato in modo improprio . C è potente e puoi sparare la testa se usi la pistola C. C ++ ha la pistola C, ma anche il bisturi, il taser e qualsiasi altro strumento che troverai nel toolkit. Puoi abusare del bisturi e sanguinare a morte. Oppure puoi creare un codice molto elegante e robusto.

Quindi, come ogni funzione C ++, ne hai davvero bisogno? È la domanda a cui devi rispondere prima di usarlo in C ++. Se non lo fai, non ti costerà nulla. Ma se ne hai davvero bisogno, almeno, la lingua non ti deluderà.

L'esempio della data?

Il tuo errore, mi sembra, sta mescolando gli operatori:

1974/01/06AD
    ^  ^  ^

Questo non può essere evitato, poiché / essendo un operatore, il compilatore deve interpretarlo. E, AFAIK, è una buona cosa.

Per trovare una soluzione al tuo problema, scriverei il letterale in qualche altro modo. Per esempio:

"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

Personalmente, sceglierei le date intere e ISO, ma dipende dalle VOSTRE esigenze. Qual è il punto centrale di permettere all'utente di definire i propri nomi letterali.


1
Grazie, io e le mie altre personalità supplenti siamo stati scoperti. Più seriamente, l'ho scritto da solo, ma forse sto usando l'espressione della mia lingua madre e non traducono bene in inglese.
paercebal,

Per quanto riguarda le "parti diverse", come mostrato dai loro titoli, scusate, immagino che questo organizzi un post piuttosto lungo. Per quanto riguarda il testo in grassetto, è il riassunto del paragrafo in cui si trovano. Le persone che desiderano solo le informazioni senza giustificazione possono limitare la lettura dei titoli e del testo in grassetto.
paercebal,

3
+1. Spiegazione davvero piacevole. Stiamo aspettando che questo venga implementato. È davvero importante per noi. Lavoriamo su MDE (Model-Driven Engineering) e riteniamo che ciò sia necessario. Sto aggiungendo una risposta di seguito per spiegare il nostro caso.
Diego Sevilla,

9
@TGV: you can write 1+2i, but you still can't write a+bi, so there's absolutely no pointAnche ignorare il tuo a+biesempio è ridicolo, il fatto che lo percepisca come "bassa frequenza" non significa che tutti lo facciano. . . Osservando il quadro generale, il punto è assicurarsi che gli oggetti definiti dall'utente possano essere il più possibile considerati cittadini di prima classe della lingua, così come i tipi incorporati. Quindi, se riesci a scrivere 1.5fe 1000UL, perché non puoi scrivere 25io addirittura 100101b? Al contrario di C e Java, i tipi di utenti non devono essere considerati cittadini di seconda classe del linguaggio in C ++.
paercebal,

3
@Anton:: Most of data still comes from IOci sono molti valori hardcoded nel codice. Guarda tutti i valori booleani, tutti i numeri interi, tutti i doppi che compaiono nel codice, perché è più conveniente scrivere x = 2 * y ;invece che x = Two * ydov'è Twouna costante fortemente tipizzata . I letterali definiti dall'utente ci consentono di inserire un tipo su di esso e scrivere: x = 2_speed * y ;e fare in modo che il compilatore verifichi che il calcolo abbia senso. . . Tutto nasce dalla digitazione forte. . . Forse non lo userai. Ma sicuramente lo farò, non appena sarò in grado di utilizzare un compilatore abilitato per C ++ 11 al lavoro.
paercebal,

36

È molto carino per il codice matematico. Fuori di testa posso vedere l'uso per i seguenti operatori:

deg per gradi. Ciò rende la scrittura degli angoli assoluti molto più intuitiva.

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

Può anche essere utilizzato per varie rappresentazioni in virgola fissa (che sono ancora in uso nel campo del DSP e della grafica).

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

Sembrano begli esempi su come usarlo. Aiutano a rendere più leggibili le costanti nel codice. È un altro strumento per rendere illeggibile anche il codice, ma abbiamo già così tanti strumenti che abusano che uno in più non fa molto male.


1
"ma abbiamo già così tanti abusi di strumenti che uno in più non fa molto male. " Wow, spero che questa non sia la filosofia alla base di tutte quelle c ++ [x] 1234567890 che inondano di recente. Immagina di dover imparare una libreria che utilizza tutti questi, oltre a dieci formati di file per la configurazione e due strumenti per pre e post-elaborazione del tuo codice ...
masterxilo,

@masterxilo Naturalmente, in realtà, il tuo esempio è assurdo: le UDL non sono più difficili da imparare delle funzioni, poiché solo la sintassi dello zucchero per loro - e inoltre, ogni buona lib usa solo le funzionalità necessarie per migliorare la UX - e documenta esattamente quello che tutti i mezzi. Se qualcuno abusa di una funzione per generare codice illeggibile (supponendo che sia del tutto evitabile nella sua linea di lavoro ...), non mette in errore quella funzione, solo l'uso. Inoltre, illeggibile di una persona è il pane e il burro di un'altra. Sono tutte opinioni - e opzioni . Se non ti piacciono, non preoccuparti! Non è necessario usarli. Gli altri possono .
underscore_d

17

Le UDL sono spaziate dal nome (e possono essere importate usando dichiarazioni / direttive, ma non è possibile esplicitamente inserire nello spazio dei nomi un letterale come 3.14std::i ), il che significa che (si spera) non ci saranno tonnellate di scontri.

Il fatto che possano effettivamente essere modellati (e rappresentati) significa che puoi fare cose abbastanza potenti con le UDL. Gli autori di Bigint saranno davvero felici, poiché possono finalmente avere costanti arbitrariamente grandi, calcolate in fase di compilazione (tramite constexpr o template).

Sono solo triste che non vedremo un paio di letterali utili nello standard (dall'aspetto di esso), come sper std::stringei per l'unità immaginaria.

La quantità di tempo di codifica che verrà salvata dagli UDL non è in realtà così elevata, ma la leggibilità sarà notevolmente aumentata e sempre più calcoli potranno essere spostati in tempo di compilazione per un'esecuzione più rapida.


Grazie per aver chiarito il punto sugli spazi dei nomi ... me lo stavo chiedendo.
Nathan Reed,

12

Vorrei aggiungere un po 'di contesto. Per il nostro lavoro, i letterali definiti dall'utente sono molto necessari. Lavoriamo su MDE (Model-Driven Engineering). Vogliamo definire modelli e metamodelli in C ++. Abbiamo effettivamente implementato una mappatura da Ecore a C ++ ( EMF4CPP ).

Il problema si presenta quando si è in grado di definire gli elementi del modello come classi in C ++. Stiamo adottando l'approccio di trasformare il metamodello (Ecore) in modelli con argomenti. Gli argomenti del modello sono le caratteristiche strutturali di tipi e classi. Ad esempio, una classe con due attributi int sarebbe qualcosa del tipo:

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

Tuttavia, si scopre che ogni elemento in un modello o metamodello, di solito ha un nome. Vorremmo scrivere:

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

MA, C ++ o C ++ 0x non lo consentono, poiché le stringhe sono vietate come argomenti per i modelli. Puoi scrivere il nome char con char, ma questo è sicuramente un casino. Con letterali definiti dall'utente, potremmo scrivere qualcosa di simile. Supponiamo che utilizziamo "_n" per identificare i nomi degli elementi del modello (non utilizzo l'esatta sintassi, solo per fare un'idea):

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

Infine, avere quelle definizioni come modelli ci aiuta molto a progettare algoritmi per attraversare gli elementi del modello, le trasformazioni del modello, ecc. Che sono davvero efficienti, perché le informazioni sul tipo, l'identificazione, le trasformazioni, ecc. Sono determinate dal compilatore in fase di compilazione.


4
Mi piace moltissimo la by the compiler at compile timeparte ... :-)
paercebal,

12

Bjarne Stroustrup parla di UDL in questa conferenza in C ++ 11 , nella prima sezione sulle interfacce ricche di tipi, circa 20 minuti.

Il suo argomento di base per le UDL assume la forma di un sillogismo:

  1. I tipi "Trivial", ovvero i tipi primitivi incorporati, possono solo rilevare errori di tipo banali. Le interfacce con tipi più ricchi consentono al sistema di tipi di rilevare più tipi di errori.

  2. I tipi di errori di tipo che il codice riccamente digitato può rilevare incidono sul codice reale. (Dà l'esempio del Mars Climate Orbiter, che fallì tristemente a causa di un errore dimensionale in una costante importante).

  3. Nel codice reale, le unità vengono utilizzate raramente. Le persone non li usano, perché incorrere in calcoli di runtime o sovraccarico di memoria per creare tipi ricchi è troppo costoso e l'utilizzo di un codice di unità modello C ++ preesistente è talmente notoriamente brutto che nessuno lo usa. (Empiricamente, nessuno lo usa, anche se le biblioteche sono in circolazione da un decennio).

  4. Pertanto, al fine di indurre gli ingegneri a utilizzare le unità in codice reale, avevamo bisogno di un dispositivo che (1) non incorre in sovraccarico di runtime e (2) sia accettabile a livello nazionale.


9

Supportare il controllo della dimensione in fase di compilazione è l'unica giustificazione richiesta.

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

assert(energy == 4_J); 

Vedere ad esempio PhysUnits-CT-Cpp11 , una piccola libreria di sola intestazione C ++ 11, C ++ 14 per analisi dimensionali in fase di compilazione e manipolazione e conversione di unità / quantità. Più semplice di Boost.Units , supporta letterali di simboli di unità come m, g, s, prefissi metrici come m, k, M, dipende solo dalla libreria C ++ standard, solo SI, potenze integrali delle dimensioni.


Oppure vedi unità , una libreria di analisi delle dimensioni e di conversione delle unità in tempo di compilazione, solo intestazione, costruita su c ++ 14 senza dipendenze di Nic Holthaus .
Martin Moene,

6

Hmm ... Non ho ancora pensato a questa funzione. Il tuo campione è stato ben pensato ed è sicuramente interessante. Il C ++ è molto potente come lo è ora, ma sfortunatamente la sintassi utilizzata nelle parti di codice che leggi è a volte eccessivamente complessa. La leggibilità è, se non tutto, almeno molto. E tale funzionalità sarebbe orientata per una maggiore leggibilità. Se prendo il tuo ultimo esempio

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

... Mi chiedo come lo esprimeresti oggi. Avresti una classe KG e una classe LB e confronteresti oggetti impliciti:

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

E anche quello farebbe. Con tipi che hanno nomi più lunghi o tipi che non hai speranze di avere un costruttore così carino per sans che scrivono un adattatore, potrebbe essere una buona aggiunta per la creazione e l'inizializzazione implicita al volo. D'altra parte, puoi già creare e inizializzare oggetti usando anche i metodi.

Ma sono d'accordo con Nils sulla matematica. Le funzioni di trigonometria C e C ++, ad esempio, richiedono input in radianti. Penso che in gradi, quindi una conversione implicita molto breve come quella pubblicata da Nils è molto bella.

Alla fine, sarà comunque zucchero sintattico, ma avrà un leggero effetto sulla leggibilità. E probabilmente sarà anche più facile scrivere alcune espressioni (sin (180.0deg) è più facile da scrivere rispetto a sin (deg (180.0)). E poi ci saranno persone che abusano del concetto. Ma poi, le persone che abusano del linguaggio dovrebbero usare linguaggi molto restrittivi piuttosto che qualcosa di espressivo come C ++.

Ah, il mio post non dice praticamente nulla tranne: andrà bene, l'impatto non sarà troppo grande. Non preoccuparti. :-)


5
Le tue parentesi sono sbilanciate! Scusa, anche il mio disturbo ossessivo compulsivo mi odia.
X-Istence,

3

Non ho mai avuto bisogno o voluto questa funzione (ma questo potrebbe essere l' effetto Blub ). La mia reazione istintiva è che è zoppo, e probabilmente fa appello alle stesse persone che pensano che sia bello sovraccaricare l'operatore + per qualsiasi operazione che potrebbe essere interpretata da remoto come aggiunta.


Confermo: articolo molto interessante.
paercebal,

2

Il C ++ di solito è molto severo riguardo alla sintassi usata - a parte il preprocessore non c'è molto che puoi usare per definire una sintassi / grammatica personalizzata. Ad esempio, possiamo sovraccaricare le operazioni esistenti, ma non possiamo definirne di nuove: l'IMO è in linea con lo spirito del C ++.

Non mi dispiace alcuni modi per un codice sorgente più personalizzato, ma il punto scelto mi sembra molto isolato, il che mi confonde di più.

Anche l'uso previsto può rendere molto più difficile la lettura del codice sorgente: una singola lettera può avere effetti collaterali di vasta portata che non possono in alcun modo essere identificati dal contesto. Con la simmetria su u, l e f, la maggior parte degli sviluppatori sceglierà lettere singole.

Ciò può anche trasformare l'ambito in un problema, l'utilizzo di singole lettere nello spazio dei nomi globale sarà probabilmente considerato una cattiva pratica e gli strumenti che si suppone che mescolino più facilmente le librerie (spazi dei nomi e identificatori descrittivi) probabilmente ne vanificheranno lo scopo.

Vedo qualche merito in combinazione con "auto", anche in combinazione con una libreria di unità come unità boost , ma non abbastanza per meritare questa adizione.

Mi chiedo, tuttavia, quali idee intelligenti veniamo fuori.


1
using single letters in global namespace will probably be considered bad practiceMa ciò non ha alcuna rilevanza: (A) le UDL devono essere definite nell'ambito (non globale) dello spazio dei nomi ... presumibilmente perché (B) devono consistere in un carattere di sottolineatura quindi> = 1 lettera, non solo la lettera e tali identificatori in gli NS globali sono riservati per l'implementazione. Questo è almeno 2 punti contro l'idea che le UDL generano in modo innato confusione. Per quanto riguarda l'ambito di applicazione dello spazio dei nomi che riduce l'utilità della funzione, ecco perché ad esempio lo stdlib li dichiara in inline namespaces che gli utenti possono importare all'ingrosso se lo desiderano.
underscore_d

2

Ho usato letterali utente per stringhe binarie come questa:

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

usando il std::string(str, n)costruttore in modo da \0non tagliare la corda a metà. (Il progetto fa molto lavoro con vari formati di file.)

Questo mi è stato utile anche quando ho abbandonato std::stringun involucro per std::vector.


-5

Il rumore di linea in quella cosa è enorme. Inoltre è orribile da leggere.

Fammi sapere, hanno ragionato quella nuova aggiunta di sintassi con qualche tipo di esempio? Ad esempio, hanno un paio di programmi che usano già C ++ 0x?

Per me, questa parte:

auto val = 3.14_i

Non giustifica questa parte:

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

Nemmeno se usassi la sintassi i anche in altre 1000 righe. Se scrivi, probabilmente scrivi anche oltre 10000 righe di qualcos'altro. Soprattutto quando probabilmente continuerai a scrivere principalmente ovunque:

std::complex<double> val = 3.14i

la parola chiave "auto" può essere giustificata però, solo forse. Ma prendiamo solo C ++, perché è meglio di C ++ 0x in questo aspetto.

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

È come ... così semplice. Anche se tutte le parentesi rigide e appuntite sono solo zoppe se la usi ovunque. Non inizio a indovinare quale sintassi ci sia in C ++ 0x per trasformare std :: complex in complex.

complex = std::complex<double>;

Questo è forse qualcosa di semplice, ma non credo sia così semplice in C ++ 0x.

typedef std::complex<double> complex;

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

Forse? > :)

Ad ogni modo, il punto è: scrivere 3.14i invece di std :: complex (0, 3.14); non ti fa risparmiare molto tempo in generale, tranne in alcuni casi super speciali.


10
@Cheery: Per te "auto val = 3.14i" non giustifica il codice scritto per supportarlo. Potrei rispondere che, per me "printf ("% i ", 25)" non giustifica il codice scritto per printf. Vedi uno schema?
paercebal,

5
@Cheery: "Il rumore di linea in quella cosa è enorme". No, non è ... "Inoltre è orribile da leggere". Il tuo argomento soggettivo è interessante, ma dovresti dare un'occhiata al sovraccarico dell'operatore in generale per vedere il codice per questa funzione è lungi dall'essere sorprendente / scioccante ... Per uno sviluppatore C ++
paercebal,

3
auto aiuterà la leggibilità. considera l'utilizzo di interatori in un ciclo for: for (auto it = vec.begin (); it! = vec.end (); ++ it) ... Conosco for_each, ma non mi piace dover creare un funzione per usarlo .
KitsuneYMG,

1
@kts: con C ++ 1x avremo lambda e intervallo per
Joe D

3
Se la tua linea di C ++ è migliore di C ++ 0x, allora la mia linea è ancora migliore. Scrivere solo: std::complex<double> val(0, 3.14);.
Ben Voigt,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.