Quando utilizzare la funzionalità constexpr in C ++ 11?


337

Mi sembra che avere una "funzione che restituisce sempre 5" stia rompendo o diluendo il significato di "chiamare una funzione". Ci deve essere un motivo o una necessità per questa funzionalità o non sarebbe in C ++ 11. Perché è lì?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

Mi sembra che se avessi scritto una funzione che restituiva un valore letterale e venissi a una revisione del codice, qualcuno mi avrebbe detto che avrei dovuto dichiarare un valore costante invece di scrivere return 5.


28
Puoi definire una funzione ricorsiva che restituisce un constexpr? In tal caso, posso vedere un utilizzo.
Il

20
Credo che la domanda dovrebbe indicare "perché introdurre una nuova parola chiave (!) Se il compilatore può dedurre da solo se una funzione può essere valutata in fase di compilazione o meno". Avere "garantito da una parola chiave" suona bene, ma penso che preferirei averlo garantito ogni volta che è possibile, senza la necessità di una parola chiave.
Kos,

6
@Kos: Qualcuno che ha più familiarità con gli interni C ++ probabilmente preferirebbe la tua domanda, ma la mia domanda proviene dal punto di vista di una persona che ha scritto codice C prima, ma non ha familiarità con le parole chiave C ++ 2011, né i dettagli di implementazione del compilatore C ++ . Essere in grado di ragionare sull'ottimizzazione del compilatore e sulla deduzione dell'espressione costante è un argomento per una domanda per utenti più avanzati di questa.
Warren P

8
@Kos Stavo pensando sulla stessa linea di te, e la risposta che mi è venuta in mente è stata, senza constexpr, come faresti (facilmente) a sapere che il compilatore ha effettivamente valutato la funzione per la compilazione in tempo per te? Suppongo che potresti controllare l'output dell'assembly per vedere cosa ha fatto, ma è più facile dire al compilatore che hai bisogno di quell'ottimizzazione e se per qualche motivo non può farlo per te, ti darà una bella compilazione- errore invece di non riuscire a ottimizzare silenziosamente dove ci si aspettava che ottimizzasse.
Jeremy Friesner,

3
@Kos: potresti dire la stessa cosa const. In effetti, l' intenzione obbligatoria è utile ! Le dimensioni dell'array sono l'esempio canonico.
Razze di leggerezza in orbita

Risposte:


303

Supponiamo che faccia qualcosa di un po 'più complicato.

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

Ora hai qualcosa che può essere valutato fino a una costante mantenendo una buona leggibilità e consentendo un'elaborazione leggermente più complessa rispetto all'impostazione di una costante su un numero.

Fondamentalmente fornisce un buon aiuto per la manutenibilità poiché diventa più ovvio ciò che stai facendo. Prendi max( a, b )ad esempio:

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

È una scelta piuttosto semplice lì, ma significa che se chiami maxcon valori costanti viene calcolato esplicitamente in fase di compilazione e non in fase di esecuzione.

Un altro buon esempio sarebbe una DegreesToRadiansfunzione. Ognuno trova gradi più facili da leggere dei radianti. Mentre potresti sapere che 180 gradi è in radianti, è molto più chiaro scritto come segue:

const float oneeighty = DegreesToRadians( 180.0f );

Molte buone informazioni qui:

http://en.cppreference.com/w/cpp/language/constexpr


18
Ottimo punto con esso che dice al compilatore di provare a calcolare il valore in fase di compilazione. Sono curioso di sapere perché const non fornisce questa funzionalità quando vengono specificate ottimizzazioni specifiche? O lo fa?
TamusJRoyce,

11
@Tamus: spesso lo farà ma non è obbligato a farlo. constexpr obbliga il compilatore e genererà un errore se non può.
Goz,

20
Ora lo vedo. Il peccato (0,5) è un altro. Questo sostituisce ordinatamente le macro C.
Warren P,

10
Vedo questo come una nuova domanda di intervista: spiega le differenze tra la parola chiave const e constexpr.
Warren P

2
Come modo per documentare questo punto per me stesso ho scritto un codice simile come sopra e ancora con la funzione che è "const" piuttosto che "constexpr". Dato che sto usando Clang3.3, -pedantic-errors e -std = c ++ 11, mi aspettavo che quest'ultimo non venisse compilato. Ha compilato ed eseguito come nel caso "constexpr". Pensi che si tratti di un'estensione di clang o c'è stata una modifica alla specifica C ++ 11 da quando è stato risposto a questo post?
Arbalest,

144

introduzione

constexprnon è stato introdotto come un modo per dire all'implementazione che qualcosa può essere valutato in un contesto che richiede un'espressione costante ; implementazioni conformi sono state in grado di dimostrarlo prima di C ++ 11.

Qualcosa che un'implementazione non può dimostrare è il intento di un certo codice:

  • Cosa vuole esprimere lo sviluppatore con questa entità?
  • Dovremmo consentire ciecamente al codice di essere utilizzato in un'espressione costante , solo perché sembra funzionare?

Cosa sarebbe il mondo senza constexpr?

Supponiamo che tu stia sviluppando una libreria e ti rendi conto che vuoi essere in grado di calcolare la somma di ogni numero intero nell'intervallo (0,N].

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

La mancanza di intenti

Un compilatore può facilmente dimostrare che la funzione sopra è richiamabile in a un'espressione costante se l'argomento passato è noto durante la traduzione; ma non hai dichiarato questo come un intento - è semplicemente successo.

Ora arriva qualcun altro, legge la tua funzione, fa la stessa analisi del compilatore; " Oh, questa funzione è utilizzabile in un'espressione costante!" e scrive il seguente pezzo di codice.

T arr[f(10)]; // freakin' magic

L'ottimizzazione

Tu, come sviluppatore di librerie "fantastico" , decidi che fdovrebbe essere memorizzato nella cache il risultato quando viene invocato; chi vorrebbe calcolare ripetutamente lo stesso insieme di valori?

int func (int n) { 
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ()) 
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

Il risultato

Introducendo la tua stupida ottimizzazione, hai semplicemente rotto ogni utilizzo della tua funzione che si trovava in un contesto in cui era richiesta un'espressione costante .

Non hai mai promesso che la funzione fosse utilizzabile in un'espressione costante e senza di constexpressa non ci sarebbe modo di fornire tale promessa.


Quindi, perché ne abbiamo bisogno constexpr?

L'uso principale di constexpr è dichiarare l' intenzione .

Se un'entità non è contrassegnata come constexpr- non è mai stata pensata per essere utilizzata in un'espressione costante ; e anche se lo è, facciamo affidamento sul compilatore per diagnosticare tale contesto (perché ignora il nostro intento).


25
Questa è probabilmente la risposta corretta, poiché le recenti modifiche in C ++ 14 e C ++ 17 consentono di utilizzare un intervallo molto più ampio del linguaggio nelle constexprespressioni. In altre parole, praticamente tutto può essere annotato constexpr(forse un giorno andrà semplicemente via per questo?), E a meno che uno non abbia un criterio su quando usare constexpro meno, praticamente tutto il codice verrà scritto come tale .
alecov,

4
@alecov Sicuramente non tutto ... I/O, syscalle dynamic memory allocationsicuramente non può essere contrassegnato come constexprInoltre, non tutto dovrebbe essere constexpr.
JiaHao Xu,

1
@alecov Alcune funzioni sono pensate per essere eseguite in fase di esecuzione e non ha senso farlo in fase di compilazione.
JiaHao Xu,

1
Mi piace anche questa risposta al meglio. La valutazione del tempo di compilazione è un'ottimizzazione ordinata, ma ciò che ottieni davvero constexprè una garanzia di un qualche tipo di comportamento. Proprio come constfa.
Tomáš Zato - Ripristina Monica

Quale compilatore consente a questa versione senza constexpr di int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)]; non riuscire a compilarla da nessuna parte?
Jer

91

Prendi std::numeric_limits<T>::max(): per qualsiasi motivo, questo è un metodo.constexprsarebbe utile qui.

Un altro esempio: si desidera dichiarare un array C (o a std::array) grande quanto un altro array. Il modo per farlo al momento è così:

int x[10];
int y[sizeof x / sizeof x[0]];

Ma non sarebbe meglio poter scrivere:

int y[size_of(x)];

Grazie a constexprpuoi:

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}

1
+1 per un uso piacevole del modello, ma funzionerebbe esattamente allo stesso modo senza constexpr, no?
Kos,

21
@Kos: No. Restituirebbe un valore di runtime. constexprforza il compilatore a fare in modo che la funzione restituisca un valore in fase di compilazione (se possibile).
deft_code

14
@Kos: senza di constexpressa non può essere utilizzato in una dichiarazione di dimensione dell'array, né come argomento modello, indipendentemente dal fatto che il risultato della chiamata di funzione sia una costante di compilazione o meno. Questi due sono fondamentalmente gli unici casi d'uso per constexprma almeno l'argomento template caso d'uso è importante.
Konrad Rudolph,

2
"per qualsiasi motivo, questo è un metodo": il motivo è che ci sono solo numeri interi di tempo di compilazione in C ++ 03, ma nessun altro tipo di tempo di compilazione, quindi solo un metodo può funzionare per tipi arbitrari prima di C ++ 11.
Sebastian Mach,

5
@LwCui No, non è "ok": GCC è solo lassista di default su certe cose. Utilizzare l' -pedanticopzione e verrà contrassegnata come errore.
Konrad Rudolph,

19

constexprle funzioni sono davvero belle e un'ottima aggiunta a c ++. Tuttavia, hai ragione nel dire che la maggior parte dei problemi che risolve può essere risolta in modo non elegante con le macro.

Tuttavia, uno degli usi di constexprnon ha costanti tipizzate equivalenti in C ++ 03.

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;

12
Potresti chiarire che "Errore del linker ESTREMAMENTE sottile"? O almeno fornire un puntatore a un chiarimento?
enobayram,

4
@enobayram, L'operatore ternario prende l'indirizzo degli operandi. Questo non è ovvio dal codice. Tutto viene compilato correttamente, ma il collegamento non riesce perché l'indirizzo di fournon si risolve. Ho dovuto scavare davvero per capire chi stava prendendo l'indirizzo della mia static constvariabile.
deft_code

23
"Questo è negativo per ovvi motivi": il motivo più ovvio è il punto e virgola, giusto?
TonyK,

4
L'errore "EXTREMELY sublink linker" mi ha completamente perplesso. Né fourfivesono portata.
Steven Lu,

3
vedi anche il nuovo enum classtipo, risolve alcuni dei problemi enum.
ninMonkey,

14

Da quello che ho letto, la necessità di constexpr deriva da un problema di metaprogrammazione. Le classi di tratti possono avere costanti rappresentate come funzioni, pensa: numeric_limits :: max (). Con constexpr, questi tipi di funzioni possono essere utilizzati nella metaprogrammazione o come limiti di array, ecc.

Un altro esempio al di sopra della mia testa sarebbe che per le interfacce di classe, potresti volere che i tipi derivati ​​definiscano le loro costanti per alcune operazioni.

Modificare:

Dopo aver cercato SO, sembra che altri abbiano escogitato alcuni esempi di ciò che potrebbe essere possibile con constexprs.


"Per far parte di un'interfaccia devi essere una funzione"?
Daniel Earwicker,

Ora che posso vedere l'utilità di questo, sono un po 'più entusiasta di C ++ 0x. Sembra una cosa ben ponderata. Sapevo che dovevano essere. Quegli esperti di lingua standard raramente fanno cose casuali.
Warren P,

Sono molto più entusiasta di lambdas, il modello di threading, initializer_list, riferimenti rvalue, modelli variadic, i nuovi sovraccarichi di bind ... c'è molto da aspettarsi.
Luca

1
Oh sì, ma capisco già lambda / chiusure in molte altre lingue. constexprè più specificamente utile in un compilatore con un potente sistema di valutazione delle espressioni in fase di compilazione. Il C ++ non ha davvero pari in quel dominio. (Questo è un grande elogio per C ++ 11, IMHO)
Warren P

11

Dal discorso di Stroustrup a "Going Native 2012":

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s’
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2’ 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human

2
Questo esempio può essere trovato anche nel documento Software Development for Infrastructure di Stroustrup .
Matthieu Poullet,

clang-3.3: errore: il tipo restituito della funzione constexpr 'Valore <Secondo>' non è un tipo letterale
Mitja

Questo è carino ma chi inserisce valori letterali in questo modo. Avere il tuo compilatore "controlla le tue unità" per te avrebbe senso se stessi scrivendo una calcolatrice interattiva.
Bobobobo,

5
@bobobobo o se stavi scrivendo un software di navigazione per Mars Climate Orbiter, forse :)
Jeremy Friesner,

1
Per farlo compilare - 1. Utilizzare il trattino basso nei suffissi letterali. 2. aggiungere l'operatore "" _m per 100_m. 3. utilizzare 100.0_m o aggiungere un sovraccarico che accetta unsigned long long. 4. Dichiarare constexpr il costruttore di valori. 5. Aggiungi l'operatore corrispondente / alla classe Value in questo modo: constexpr auto operator / (valore const <Y> e altro) const {valore restituito <Unità <TheUnit :: m - Value <Y> :: TheUnit :: m, TheUnit :: kg - Valore <Y> :: TheUnit :: kg, TheUnit :: s - Valore <Y> :: TheUnit :: s >> (val / other.val); }. Dove TheUnit è tipedef per Unit aggiunta all'interno della classe Value.
0kats

8

Un altro uso (non ancora menzionato) sono i constexprcostruttori. Ciò consente di creare costanti di tempo di compilazione che non devono essere inizializzate durante il runtime.

const std::complex<double> meaning_of_imagination(0, 42); 

Abbinalo a valori letterali definiti dall'utente e avrai pieno supporto per le classi letterali definite dall'utente.

3.14D + 42_i;

6

C'era un modello con metaprogrammazione:

template<unsigned T>
struct Fact {
    enum Enum {
        VALUE = Fact<T-1>*T;
    };
};

template<>
struct Fact<1u> {
    enum Enum {
        VALUE = 1;
    };
};

// Fact<10>::VALUE is known be a compile-time constant

Credo che sia constexprstato introdotto per permetterti di scrivere tali costrutti senza la necessità di modelli e strani costrutti con specializzazione, SFINAE e roba del genere - ma esattamente come se dovessi scrivere una funzione di runtime, ma con la garanzia che il risultato sarà determinato nella compilazione -tempo.

Tuttavia, si noti che:

int fact(unsigned n) {
    if (n==1) return 1;
    return fact(n-1)*n;
}

int main() {
    return fact(10);
}

Compilalo con g++ -O3e lo vedraifact(10) viene effettivamente evocato in fase di compilazione!

Un compilatore compatibile con VLA (quindi un compilatore C in modalità C99 o compilatore C ++ con estensioni C99) può anche consentire di fare:

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

Ma al momento è C ++ non standard - constexprsembra un modo per combatterlo (anche senza VLA, nel caso precedente). E c'è ancora il problema della necessità di avere espressioni costanti "formali" come argomenti modello.


La funzione fact non viene valutata in fase di compilazione. Deve essere constexpr e deve avere una sola dichiarazione di ritorno.
Sumant,

1
@Sumant: hai ragione, non è necessario valutarlo in fase di compilazione, ma lo è! Mi riferivo a ciò che accade realmente nei compilatori. Compilalo sul GCC recente, guarda l'asm risultante e controlla tu stesso se non mi credi!
Kos,

Prova ad aggiungere std::array<int, fact(2)>e vedrai che fact () non viene valutato in fase di compilazione. È solo l'ottimizzatore GCC che fa un buon lavoro.

1
È quello che ho detto ... sono davvero così poco chiaro? Vedi l'ultimo paragrafo
Kos,

5

Ho appena iniziato a passare da un progetto a c ++ 11 e ho trovato una situazione perfettamente valida per constexpr che ripulisce metodi alternativi per eseguire la stessa operazione. Il punto chiave qui è che puoi inserire la funzione nella dichiarazione di dimensione dell'array solo quando è dichiarata constexpr. Ci sono un certo numero di situazioni in cui posso vedere che questo è molto utile andare avanti con l'area del codice in cui sono coinvolto.

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}

4
Questo potrebbe essere scritto allo stesso modo: const size_t MaxIPV4StringLength = sizeof ("255.255.255.255");
Superfly Jon,

static inline constexpr const autoprobabilmente è meglio.
JiaHao Xu,

3

Tutte le altre risposte sono fantastiche, voglio solo fare un bell'esempio di una cosa che puoi fare con constexpr che è sorprendente. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) è un parser HTML e motore di template in fase di compilazione. Questo significa che puoi inserire HTML ed estrarre un albero che può essere manipolato. L'analisi effettuata al momento della compilazione può darti un po 'di prestazioni extra.

Dall'esempio della pagina github:

#include <iostream>
#include "seephit.h"
using namespace std;



int main()
{
  constexpr auto parser =
    R"*(
    <span >
    <p  color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p  >
    </span>
    )*"_html;

  spt::tree spt_tree(parser);

  spt::template_dict dct;
  dct["name"] = "Mary";
  dct["profession"] = "doctor";
  dct["city"] = "London";

  spt_tree.root.render(cerr, dct);
  cerr << endl;

  dct["city"] = "New York";
  dct["name"] = "John";
  dct["profession"] = "janitor";

  spt_tree.root.render(cerr, dct);
  cerr << endl;
}

1

Il tuo esempio di base serve lo stesso argomento di quello delle costanti stesse. Perché usare

static const int x = 5;
int arr[x];

al di sopra di

int arr[5];

Perché è molto più mantenibile. L'uso di constexpr è molto, molto più veloce da scrivere e leggere rispetto alle tecniche di metaprogrammazione esistenti.


0

Può abilitare alcune nuove ottimizzazioni. consttradizionalmente è un suggerimento per il sistema di tipi e non può essere utilizzato per l'ottimizzazione (ad esempio una constfunzione membro può const_caste modificare comunque l'oggetto, legalmente, quindiconst non ci si può fidare dell'ottimizzazione).

constexprsignifica che l'espressione è davvero costante, a condizione che gli input per la funzione siano const. Tener conto di:

class MyInterface {
public:
    int GetNumber() const = 0;
};

Se questo è esposto in qualche altro modulo, il compilatore non può fidarsi che GetNumber()non restituirà valori diversi ogni volta che viene chiamato - anche consecutivamente senza chiamate non cost in mezzo - perchéconst avrebbe potuto essere gettato via nell'implementazione. (Ovviamente qualsiasi programmatore che ha fatto questo dovrebbe essere girato, ma il linguaggio lo consente, quindi il compilatore deve rispettare le regole.)

Aggiunta constexpr:

class MyInterface {
public:
    constexpr int GetNumber() const = 0;
};

Il compilatore può ora applicare un'ottimizzazione in cui il valore restituito GetNumber()è memorizzato nella cache ed eliminare le chiamate aggiuntive GetNumber(), poiché constexprè una garanzia più forte che il valore restituito non cambierà.


In realtà const può essere utilizzato nell'ottimizzazione ... È un comportamento indefinito modificare un valore definito const anche dopo un const_castIIRC. Mi aspetto che sia coerente per le constfunzioni membro, ma dovrei verificarlo con lo standard. Ciò significherebbe che il compilatore può fare in modo sicuro ottimizzazioni lì.
Kos,

1
@Warren: non importa se l'ottimizzazione viene effettivamente eseguita, è solo consentita. @Kos: è una sottigliezza poco nota che se l' oggetto originale non è stato dichiarato const ( int xvs. const int x), allora è sicuro modificarlo const_casteliminando const su un puntatore / riferimento ad esso. Altrimenti, const_castinvocherebbe sempre un comportamento indefinito e sarebbe inutile :) In questo caso, il compilatore non ha informazioni sulla costanza dell'oggetto originale, quindi non può dirlo.
AshleysBrain

@Kos Non credo che const_cast sia l'unico problema qui. Il metodo const è autorizzato a leggere e persino a modificare una variabile globale. Al contrario, qualcuno dal thread di un altro potrebbe anche modificare l'oggetto const tra le chiamate.
enobayram,

1
"= 0" non è valido qui e deve essere rimosso. Lo farei da solo, ma non sono sicuro che sia conforme al protocollo SO.
KnowItAllWannabe,

Entrambi gli esempi non sono validi: il primo ( int GetNumber() const = 0;) dovrebbe dichiarare il GetNumber()metodo virtuale. Il secondo ( constexpr int GetNumber() const = 0;) non è valido perché lo specificatore puro ( = 0) implica che il metodo sia virtuale, ma quelli di constexpr non devono essere virtuali (rif: en.cppreference.com/w/cpp/language/constexpr )
stj

-1

Quando usare constexpr:

  1. ogni volta che c'è una costante di tempo di compilazione.

Mentre sono d'accordo con te, questa risposta non spiega perché constexpr dovrebbe essere preferito rispetto alle macro del preprocessore o const.
Sneftel,

-3

È utile per qualcosa del genere

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

int some_arr[MeaningOfLife()];

Collegalo a una classe di tratti o simili e diventa abbastanza utile.


4
Nel tuo esempio offre zero vantaggi rispetto a una costante semplice, quindi non risponde realmente alla domanda.
jalf

Questo è un esempio inventato, immagina se DefinitionOfLife () ottiene il suo valore da qualche altra parte, ad esempio un'altra funzione o un #define o una serie di questi. Potresti non sapere cosa restituisce, potrebbe essere un codice di libreria. Altri esempi, immaginiamo un contenitore immutabile che ha un metodo constexpr size (). Ora puoi fare int arr [container.size ()];
Plivesey,

2
@plivesey puoi per favore modificare la tua risposta con un esempio migliore allora.
Mukesh,
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.