Decltype e parentesi C ++ - perché?


32

L'argomento è stato discusso in precedenza , ma questo non è un duplicato.

Quando qualcuno chiede la differenza tra decltype(a)e decltype((a)), la solita risposta è - aè una variabile, (a)è un'espressione. Trovo questa risposta insoddisfacente.

Innanzitutto, aè anche un'espressione. Le opzioni per un'espressione primaria includono, tra le altre cose:

  • (espressione)
  • id-espressione

Ancora più importante, il fraseggio per declinare considera le parentesi molto, molto esplicitamente :

For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1)  if e is an unparenthesized id-expression naming a structured binding, ...
(1.2)  otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3)  otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4)  otherwise, ...

Quindi la domanda rimane. Perché le parentesi sono trattate in modo diverso? Qualcuno ha familiarità con documenti tecnici o discussioni di comitato dietro di esso? La considerazione esplicita delle parentesi porta a pensare che questa non sia una svista, quindi ci deve essere un motivo tecnico che mi manca.


2
"la solita risposta è - a è una variabile, (a) è un'espressione" Quello che vogliono dire è " (a)è un'espressione, aè un'espressione e una variabile".
HolyBlackCat

Risposte:


18

Non è una svista. È interessante notare che in Decltype e auto (revisione 4) (N1705 = 04-0145) c'è un'affermazione:

Le regole del decltype ora lo dichiarano esplicitamente decltype((e)) == decltype(e)(come suggerito da EWG).

Ma in Decltype (revisione 6): formulazione proposta (N2115 = 06-018) una delle modifiche è

L'espressione tra parentesi all'interno del decltype non è considerata come un id-expression.

Non ci sono fondamenti logici nella formulazione, ma suppongo che questa sia una specie di estensione del decltype usando una sintassi un po 'diversa, in altre parole, si intendeva differenziare questi casi.

L'utilizzo per questo è mostrato in C ++ draft9.2.8.4:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

Ciò che è veramente interessante, è come funziona con l' returnaffermazione:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

Visual Studio 2019 mi suggerisce di rimuovere la parentesi ridondante, ma in realtà si trasformano in decltype((i))quali modifiche restituiscono il valore a int&cui lo rende UB dal ritorno di una variabile locale.


Grazie per aver indicato l'origine! Decltype non solo avrebbe potuto essere specificato diversamente, ma inizialmente lo era. Il documento rev-6 aggiunge esplicitamente questo divertente comportamento tra parentesi, ma salta la logica :(. Immagino che sia così vicino a una risposta come avremmo ora ..
Ofek Shilon

Eppure nessuno ha proposto di risolvere il problema sciocco trasformandolo in due parole chiave distinte, per due funzioni distinte? Uno dei più grandi errori del comitato (che storicamente ha commesso molti errori non forzati).
curiousguy

13

Perché le parentesi sono trattate in modo diverso?

Le parentesi non sono trattate diversamente. È l'espressione id non identificata che viene trattata in modo diverso.

Quando sono presenti le parentesi, si applicano le regole regolari per tutte le espressioni. Il tipo e la categoria di valore vengono estratti e codificati nel tipo di decltype.

La disposizione speciale è lì per poter scrivere codice utile più facilmente. Quando si applica decltypeal nome di una variabile (membro), di solito non si desidera alcun tipo che rappresenti le proprietà della variabile quando viene trattata come un'espressione. Invece vogliamo solo il tipo con cui viene dichiarata la variabile, senza dover applicare una tonnellata di tratti di tipo per ottenerlo. Ed è esattamente ciò che decltypeè specificato per darci.

Se ci preoccupiamo delle proprietà della variabile come espressione, allora possiamo ancora ottenerla piuttosto facilmente, con una coppia extra di parentesi.


Quindi, per un intmembro idi a, decltype(a.i)è intwhile decltype((a.i))is int&(supponendo di ano const)? Dal momento che l'espressione a.iè assegnabile?
n314159

1
@ n314159 - Questa è la sostanza. L'espressione a.iè un valore non costante, quindi si ottiene un tipo di riferimento per valore non costante per (a.i).
StoryTeller - Unslander Monica

Perché "le regole regolari per tutte le espressioni" indicano che il loro tipo è un riferimento? Perché "le proprietà della variabile come espressione" includono la proprietà di essere un riferimento? È un default naturale?
Ofek Shilon,

1
@OfekShilon - Il loro tipo non è un riferimento. Il tipo di un'espressione non viene mai analizzato come riferimento . Ma il dectlype può risolversi solo in un tipo, ed è pensato per dirci non solo il tipo di un'espressione, ma anche la sua categoria di valore. La categoria di valore è codificata per tipi di riferimento. i valori sono &, i valori x sono &&e i valori non sono tipi di riferimento.
StoryTeller - Unslander Monica

@StoryTeller esattamente, non c'è differenza 'naturale' tra tipi di espressioni e non espressioni. Il che rende artificiale questa distinzione.
Ofek Shilon,

1

Prima del C ++ 11, il linguaggio ha bisogno degli strumenti per ottenere due diversi tipi di informazioni :

  • il tipo di un'espressione
  • il tipo di una variabile così come è stata dichiarata

A causa della natura di queste informazioni, è stato necessario aggiungere le funzionalità nella lingua (non è possibile eseguirle in una libreria). Ciò significa nuove parole chiave. Lo standard avrebbe potuto introdurre due nuove parole chiave per questo. Ad esempio exprtypeper ottenere il tipo di un'espressione e decltypeottenere il tipo di dichiarazione di una variabile. Sarebbe stata l'opzione chiara e felice.

Tuttavia, il comitato standard ha sempre fatto del suo meglio per evitare di introdurre nuove parole chiave nella lingua al fine di ridurre al minimo la rottura del vecchio codice. La compatibilità con le versioni precedenti è una filosofia di base del linguaggio.

Quindi, con C ++ 11 abbiamo ottenuto solo una parola chiave utilizzata per due cose diverse: decltype. Il modo in cui differenzia tra i due usi è trattando decltype(id-expression)diversamente. È stata una decisione consapevole da parte del comitato, un (piccolo) compromesso.


Ricordo di aver sentito questo in un discorso cpp. Tuttavia non ho alcuna speranza di trovare la fonte. Sarebbe bello se qualcuno lo trovasse.
Bolov,

1
Il C ++ ha storicamente aggiunto un sacco di nuove parole chiave, molte senza un carattere di sottolineatura e alcune con una parola inglese. Il razionale è davvero assurdo.
curioso

@curiousguy ogni parola chiave introdotta si scontrerà con i simboli utente esistenti, quindi il comitato attribuisce grande importanza alla decisione di aggiungere nuove parole chiave riservate alla lingua. Non è assurdo imo.
Bolov,

Non è vero: è exportstato introdotto. Se puoi export(in precedenza tutti i modelli erano "esportati" per impostazione predefinita), puoi avere cose come decltypee constexpr. Ovviamente l'aggiunta registerin un'altra lingua sarebbe problematica.
curiousguy

@bolov Grazie! (1) Questa è speculazione o conoscenza? Sei a conoscenza di discussioni in cui la motivazione è stata evitare una parola chiave in più? (2) Puoi fare un esempio in cui un'espressione id deve effettivamente essere trattata in modo diverso se viene usata "come espressione"? (Che cosa significa anche questo?)
Ofek Shilon
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.