Non erediterai da std :: vector


189

Ok, questo è davvero difficile da confessare, ma al momento ho una forte tentazione da cui ereditare std::vector.

Ho bisogno di circa 10 algoritmi personalizzati per il vettore e voglio che siano direttamente membri del vettore. Ma naturalmente voglio anche avere il resto std::vectordell'interfaccia. Bene, la mia prima idea, come cittadino rispettoso della legge, era di avere un std::vectormembro in MyVectorclasse. Ma poi dovrei riprovare manualmente tutta l'interfaccia di std :: vector. Troppo da scrivere. Successivamente, ho pensato all'eredità privata, così che invece di riprovare i metodi avrei scritto un mucchio di elementi using std::vector::membernella sezione pubblica. Questo è noioso in realtà.

Ed eccomi, penso davvero di poter semplicemente ereditare pubblicamente da std::vector, ma fornire un avvertimento nella documentazione che questa classe non dovrebbe essere usata polimorficamente. Penso che la maggior parte degli sviluppatori sia abbastanza competente da capire che questo non dovrebbe essere usato polimorficamente comunque.

La mia decisione è assolutamente ingiustificabile? Se è così, perché? Potete fornire un'alternativa che avrebbe effettivamente membri aggiuntivi ma che non implicherebbe la riscrittura dell'interfaccia di Vector? Ne dubito, ma se puoi, sarò solo felice.

Inoltre, a parte il fatto che qualche idiota può scrivere qualcosa del genere

std::vector<int>* p  = new MyVector

c'è qualche altro pericolo realistico nell'uso di MyVector? Dicendo realistico scarto cose come immaginare una funzione che porta un puntatore al vettore ...

Bene, ho dichiarato il mio caso. Ho peccato. Ora tocca a te perdonarmi o no :)


9
Quindi, in pratica, chiedi se è giusto violare una regola comune basata sul fatto che sei troppo pigro per implementare nuovamente l'interfaccia del contenitore? Quindi no, non lo è. Vedi, puoi avere il meglio di entrambi i mondi se ingerisci quella pillola amara e la fai correttamente. Non essere quel ragazzo. Scrivi un codice affidabile.
Jim Brissom,

7
Perché non puoi / non vuoi aggiungere la funzionalità di cui hai bisogno con funzioni non membro? Per me, sarebbe la cosa più sicura da fare in questo scenario.
Simone,

11
L' std::vectorinterfaccia di @Jim: è piuttosto vasta e, quando arriverà C ++ 1x, si espanderà notevolmente. È molto da scrivere e altro da espandere in pochi anni. Penso che questa sia una buona ragione per considerare l'eredità anziché il contenimento - se si segue la premessa che tali funzioni dovrebbero essere membri (di cui dubito). La regola per non derivare dai contenitori STL è che non sono polimorfici. Se non li stai utilizzando in questo modo, non si applica.
sbi,

9
La vera questione della domanda è nell'unica frase: "Voglio che siano direttamente membri del vettore". Nient'altro nella questione conta davvero. Perché "vuoi" questo? Qual è il problema con il semplice fornire questa funzionalità come non membri?
jalf

8
@JoshC: "Tu devi" è sempre stato più comune di "Devi", ed è anche la versione trovata nella Bibbia di King James (che è generalmente ciò a cui le persone alludono quando scrivono "non devi [...] "). Cosa mai ti porterebbe a chiamarlo "errore di ortografia"?
Ruakh,

Risposte:


155

In realtà, non c'è nulla di sbagliato nell'eredità pubblica di std::vector. Se ne hai bisogno, fallo e basta.

Suggerirei di farlo solo se è veramente necessario. Solo se non puoi fare ciò che vuoi con le funzioni gratuite (ad esempio, dovresti mantenere un certo stato).

Il problema è che si MyVectortratta di una nuova entità. Significa che un nuovo sviluppatore C ++ dovrebbe sapere che diavolo è prima di usarlo. Qual è la differenza tra std::vectore MyVector? Quale è meglio usare qua e là? Che cosa succede se ho bisogno di spostare std::vectora MyVector? Posso semplicemente usare swap()o no?

Non produrre nuove entità solo per rendere qualcosa di meglio. Queste entità (specialmente quelle così comuni) non vivranno nel vuoto. Vivranno in un ambiente misto con entropia in costante aumento.


7
La mia unica controargomentazione a questo è che bisogna davvero sapere cosa sta facendo per farlo. Ad esempio, non introdurre membri di dati aggiuntivi MyVectore quindi provare a passarlo a funzioni che accettano std::vector&o std::vector*. Se c'è qualche tipo di assegnazione di copia coinvolta usando std :: vector * o std :: vector &, abbiamo problemi di suddivisione in cui i nuovi membri dei dati MyVectornon verranno copiati. Lo stesso sarebbe vero nel chiamare lo scambio tramite un puntatore / riferimento di base. Tendo a pensare che qualsiasi tipo di gerarchia ereditaria che rischia il taglio degli oggetti sia negativo.
puzzolente472

13
std::vectorIl distruttore non lo è virtual, quindi non dovresti mai ereditarne
André Fratelli,

2
Ho creato una classe che ha ereditato pubblicamente std :: vector per questo motivo: avevo un vecchio codice con una classe vettoriale non STL e volevo passare a STL. Ho reimplementato la vecchia classe come classe derivata di std :: vector, permettendomi di continuare a usare i vecchi nomi di funzione (ad es. Count () anziché size ()) nel vecchio codice, mentre scrivevo il nuovo codice usando lo std :: vector funzioni. Non ho aggiunto alcun membro di dati, quindi il distruttore di std :: vector ha funzionato bene per gli oggetti creati nell'heap.
Graham Asher,

3
@GrahamAsher Se si elimina un oggetto tramite un puntatore alla base e il distruttore non è virtuale, il programma mostra un comportamento indefinito. Un possibile risultato di un comportamento indefinito è "ha funzionato bene nei miei test". Un altro è che invia a tua nonna la tua cronologia di navigazione web. Entrambi sono conformi allo standard C ++. Anche il passaggio dall'una all'altra con rilasci puntuali di compilatori, sistemi operativi o fasi lunari è conforme.
Yakk - Adam Nevraumont,

2
@GrahamAsher No, ogni volta che elimini un oggetto attraverso un puntatore alla base senza un distruttore virtuale, questo è un comportamento indefinito in base allo standard. Capisco cosa pensi stia succedendo; ti capita di sbagliare. "Il distruttore della classe base viene chiamato e funziona" è un possibile sintomo (e il più comune) di questo comportamento indefinito, perché quello è il codice macchina ingenuo che il compilatore di solito genera. Questo non lo rende sicuro né una grande idea da fare.
Yakk - Adam Nevraumont,

92

L'intero STL è stato progettato in modo tale che algoritmi e contenitori siano separati .

Ciò ha portato a un concetto di diversi tipi di iteratori: const iteratori, iteratori ad accesso casuale, ecc.

Pertanto ti consiglio di accettare questa convenzione e progettare i tuoi algoritmi in modo tale che non si preoccupino di quale sia il contenitore su cui stanno lavorando - e richiederebbero solo un tipo specifico di iteratore di cui avrebbero bisogno per eseguire il loro operazioni.

Inoltre, lascia che ti reindirizzi ad alcune buone osservazioni di Jeff Attwood .


63

Il motivo principale per non ereditare std::vectorpubblicamente è l'assenza di un distruttore virtuale che ti impedisce efficacemente l'uso polimorfico dei discendenti. In particolare, si è non è permesso a deleteuna std::vector<T>*che in realtà i punti in un oggetto derivato (anche se la classe derivata non aggiunge membri), ma il compilatore in genere non si può mettere in guardia su di esso.

L'eredità privata è consentita in queste condizioni. Pertanto consiglio di utilizzare l'eredità privata e l'inoltro dei metodi richiesti dal genitore, come mostrato di seguito.

class AdVector: private std::vector<double>
{
    typedef double T;
    typedef std::vector<double> vector;
public:
    using vector::push_back;
    using vector::operator[];
    using vector::begin;
    using vector::end;
    AdVector operator*(const AdVector & ) const;
    AdVector operator+(const AdVector & ) const;
    AdVector();
    virtual ~AdVector();
};

Dovresti prima considerare il refactoring dei tuoi algoritmi per astrarre il tipo di contenitore su cui stanno operando e lasciarli come funzioni di template gratuite, come sottolineato dalla maggior parte dei rispondenti. Questo di solito viene fatto facendo in modo che un algoritmo accetti una coppia di iteratori anziché contenitore come argomenti.


IIUC, l'assenza di un distruttore virtuale è solo un problema se la classe derivata alloca risorse che devono essere liberate al momento della distruzione. (Non verrebbero liberati in un caso d'uso polimorfico perché un contesto che prende inconsapevolmente la proprietà di un oggetto derivato tramite il puntatore alla base chiamerebbe il distruttore di base solo quando è il momento.) Problemi simili sorgono da altre funzioni membro ignorate, quindi attenzione deve si consideri che quelli di base sono validi per chiamare. Ma in assenza di risorse aggiuntive, ci sono altri motivi?
Peter - Ripristina Monica il

2
vectorLa memoria allocata non è il problema - dopotutto, vectoril distruttore verrebbe chiamato con un puntatore a vector. È solo che lo standard vieta deletegli oggetti negozio gratuiti attraverso un'espressione di classe base. Il motivo è sicuramente che il meccanismo di (de) allocazione potrebbe tentare di dedurre la dimensione del blocco di memoria da liberare dall'operando deletedell'operando, ad esempio quando ci sono diverse arene di allocazione per oggetti di determinate dimensioni. Questa restrizione, afaics, non si applica alla normale distruzione di oggetti con durata di conservazione statica o automatica.
Peter - Ripristina Monica il

@DavisHerring Penso che siamo d'accordo lì :-).
Peter - Ripristina Monica il

@DavisHerring Ah, vedo, ti riferisci al mio primo commento - c'era un IIUC in quel commento, ed è finito con una domanda; Ho visto più tardi che in effetti è sempre vietato. (Basilevs aveva fatto una dichiarazione generale, "previene efficacemente", e mi chiedevo quale fosse il modo specifico in cui impedisce.) Quindi sì, siamo d'accordo: UB.
Peter - Ripristina Monica il

@Basilevs Deve essere stato involontario. Fisso.
ThomasMcLeod,

36

Se stai considerando questo, hai chiaramente già ucciso i pedanti della lingua nel tuo ufficio. Con loro fuori mano, perché non farlo

struct MyVector
{
   std::vector<Thingy> v;  // public!
   void func1( ... ) ; // and so on
}

Ciò eviterà tutti i possibili errori che potrebbero derivare dall'upgrade accidentale della tua classe MyVector e puoi ancora accedere a tutte le operazioni vettoriali semplicemente aggiungendo un po ' .v.


Ed esporre contenitori e algoritmi? Vedi la risposta di Kos sopra.
bruno nery,


19

Cosa speri di realizzare? Stai solo fornendo alcune funzionalità?

Il modo idiomatico C ++ per farlo è semplicemente scrivere alcune funzioni gratuite che implementano la funzionalità. È probabile che tu non abbia davvero bisogno di uno std :: vector, in particolare per la funzionalità che stai implementando, il che significa che stai effettivamente perdendo la riusabilità cercando di ereditare da std :: vector.

Ti consiglio vivamente di consultare la libreria e le intestazioni standard e di meditare su come funzionano.


5
Non sono convinto. Potresti aggiornare con alcuni dei codici proposti per spiegare perché?
Karl Knechtel,

6
@Armen: a parte l'estetica, ci sono delle buone ragioni?
snemarch,

12
@Armen: una migliore estetica e una maggiore genericità sarebbe quella di fornire anche funzioni fronte backfunzioni gratuite . :) (Considera anche l'esempio di free begine endin C ++ 0x e boost.)
UncleBens

3
Continuo a non capire cosa c'è che non va nelle funzioni gratuite. Se non ti piace l '"estetica" della STL, forse il C ++ è il posto sbagliato per te, esteticamente. E l'aggiunta di alcune funzioni membro non lo risolverà, poiché molti altri algoritmi sono ancora funzioni gratuite.
Frank Osterfeld,

17
È difficile memorizzare nella cache il risultato di un'operazione pesante in un algoritmo esterno. Supponiamo di dover calcolare una somma di tutti gli elementi nel vettore o di risolvere un'equazione polinomiale con elementi vettoriali come coefficienti. Quelle operazioni sono pesanti e la pigrizia sarebbe utile per loro. Ma non puoi introdurlo senza avvolgere o ereditare dal contenitore.
Basilevs,

14

Penso che pochissime regole dovrebbero essere seguite alla cieca il 100% delle volte. Sembra che tu ci abbia pensato molto e siamo convinti che questa sia la strada da percorrere. Quindi - a meno che qualcuno non abbia buone ragioni specifiche per non farlo - penso che dovresti andare avanti con il tuo piano.


9
La tua prima frase è vera il 100% delle volte. :)
Steve Fallows il

5
Sfortunatamente, la seconda frase non lo è. Non ci ha pensato molto. Gran parte della domanda è irrilevante. L'unica parte di ciò che mostra la sua motivazione è "Voglio che siano direttamente membri del vettore". Voglio. Nessun motivo per cui questo è desiderabile. Sembra che non ci abbia pensato affatto .
jalf

7

Non c'è motivo di ereditare a std::vectormeno che non si desideri creare una classe che std::vectorfunzioni diversamente da , perché gestisce a modo suo i dettagli nascosti della std::vectordefinizione, o se non si hanno ragioni ideologiche per usare gli oggetti di tale classe al posto di std::vectorquelli. Tuttavia, i creatori dello standard su C ++ non hanno fornito std::vectoralcuna interfaccia (sotto forma di membri protetti) che tale classe ereditata potesse sfruttare per migliorare il vettore in un modo specifico. In effetti, non avevano modo di pensare a nessun aspetto specifico che potrebbe aver bisogno di estensione o messa a punto di un'implementazione aggiuntiva, quindi non avevano bisogno di pensare a fornire tale interfaccia per qualsiasi scopo.

Le ragioni della seconda opzione possono essere solo ideologiche, perché std::vectornon sono polimorfiche, e altrimenti non vi è alcuna differenza se si espone std::vectorl'interfaccia pubblica tramite eredità pubblica o appartenenza pubblica. (Supponiamo che sia necessario mantenere un certo stato nel proprio oggetto in modo da non poter cavarsela con funzioni gratuite). Su una nota meno solida e dal punto di vista ideologico, sembra che std::vectors siano una sorta di "idea semplice", quindi qualsiasi complessità sotto forma di oggetti di diverse possibili classi al loro posto ideologicamente non serve.


Bella risposta. Benvenuti in SO!
Armen Tsirunyan,

4

In termini pratici: se non si dispone di membri di dati nella classe derivata, non si riscontrano problemi, nemmeno nell'uso polimorfico. È necessario un distruttore virtuale solo se le dimensioni della classe base e della classe derivata sono diverse e / o si dispone di funzioni virtuali (il che significa una v-table).

MA in teoria: da [expr.delete] nell'FCD C ++ 0x: nella prima alternativa (elimina oggetto), se il tipo statico dell'oggetto da eliminare è diverso dal suo tipo dinamico, il tipo statico deve essere un la classe base del tipo dinamico dell'oggetto da eliminare e il tipo statico deve avere un distruttore virtuale o il comportamento non è definito.

Ma puoi derivare privatamente da std :: vector senza problemi. Ho usato il seguente modello:

class PointVector : private std::vector<PointType>
{
    typedef std::vector<PointType> Vector;
    ...
    using Vector::at;
    using Vector::clear;
    using Vector::iterator;
    using Vector::const_iterator;
    using Vector::begin;
    using Vector::end;
    using Vector::cbegin;
    using Vector::cend;
    using Vector::crbegin;
    using Vector::crend;
    using Vector::empty;
    using Vector::size;
    using Vector::reserve;
    using Vector::operator[];
    using Vector::assign;
    using Vector::insert;
    using Vector::erase;
    using Vector::front;
    using Vector::back;
    using Vector::push_back;
    using Vector::pop_back;
    using Vector::resize;
    ...

3
"Hai solo bisogno di un distruttore virtuale se le dimensioni della classe base e della classe derivata sono diverse e hai funzioni virtuali (il che significa una v-table)." Questa affermazione è praticamente corretta, ma non teoricamente
Armen Tsirunyan il

2
sì, in linea di principio è ancora un comportamento indefinito.
jalf

Se affermi che si tratta di un comportamento indefinito, vorrei vedere una prova (citazione dallo standard).
hmuelner,

8
@hmuelner: Sfortunatamente, Armen e Jalf hanno ragione su questo. Dal [expr.delete]nel C ++ 0x FCD: <quote> Nella prima alternativa (oggetto di eliminazione), se il tipo statica dell'oggetto da eliminare è diverso dal suo tipo dinamico, tipo statico deve essere una classe di base di tipo dinamico dell'oggetto da eliminare e il tipo statico deve avere un distruttore virtuale o il comportamento non è definito. </quote>
Ben Voigt,

1
Il che è divertente, perché in realtà pensavo che il comportamento dipendesse dalla presenza di un distruttore non banale (in particolare, che le classi POD potevano essere distrutte tramite un puntatore alla base).
Ben Voigt,

3

Se segui un buon stile C ++, l'assenza di funzione virtuale non è il problema, ma lo slicing (vedi https://stackoverflow.com/a/14461532/877329 )

Perché l'assenza di funzioni virtuali non è il problema? Perché una funzione non dovrebbe provare a deletenessun puntatore che riceve, dal momento che non ne possiede una proprietà. Pertanto, se si seguono rigide politiche di proprietà, i distruttori virtuali non dovrebbero essere necessari. Ad esempio, questo è sempre sbagliato (con o senza distruttore virtuale):

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    delete obj;
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj); //Will crash here. But caller does not know that
//  ...
    }

Al contrario, funzionerà sempre (con o senza distruttore virtuale):

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj);
//  The correct destructor *will* be called here.
    }

Se l'oggetto viene creato da una factory, la factory dovrebbe anche restituire un puntatore a un deleter funzionante, che dovrebbe essere usato al posto di delete, poiché la factory può usare il proprio heap. Il chiamante può ottenerlo sotto forma di un share_ptro unique_ptr. In breve, non deletetutto ciò che non ha ottenuto direttamente da new.


2

Sì, è sicuro finché stai attento a non fare le cose che non sono sicure ... Non penso di aver mai visto nessuno usare un vettore con nuovo, quindi in pratica probabilmente starai bene. Tuttavia, non è il linguaggio comune in c ++ ....

Sei in grado di fornire maggiori informazioni su quali sono gli algoritmi?

A volte finisci per percorrere una strada con un disegno e poi non riesci a vedere gli altri percorsi che potresti aver preso - il fatto che dichiari di aver bisogno di vector con 10 nuovi algoritmi suona campanelli d'allarme per me - ci sono davvero 10 scopi generali algoritmi che un vettore può implementare o stai cercando di creare un oggetto che è sia un vettore generico E che contiene funzioni specifiche dell'applicazione?

Non sto certo dicendo che non dovresti farlo, è solo che con le informazioni che hai suonato le campane di allarme mi fanno pensare che forse qualcosa non va nelle tue astrazioni e c'è un modo migliore per ottenere ciò che volere.


2

Inoltre ho ereditato di std::vectorrecente e l'ho trovato molto utile e finora non ho riscontrato alcun problema.

La mia classe è una classe di matrice sparsa, il che significa che ho bisogno di memorizzare i miei elementi di matrice da qualche parte, vale a dire in un std::vector. La mia ragione per ereditare era che ero un po 'troppo pigro per scrivere interfacce per tutti i metodi e inoltre sto interfacciaendo la classe su Python tramite SWIG, dove esiste già un buon codice di interfaccia std::vector. Ho trovato molto più semplice estendere questo codice di interfaccia alla mia classe piuttosto che scriverne uno nuovo da zero.

L'unico problema che posso vedere con l'approccio non è tanto con il distruttore non virtuale, ma piuttosto alcuni altri metodi, che vorrei di sovraccarico, come ad esempio push_back(), resize(), insert()ecc eredità privata potrebbe effettivamente essere una buona opzione.

Grazie!


10
Nella mia esperienza, il peggior danno a lungo termine è spesso causato da persone che provano qualcosa di sconsiderato e " finora non hanno riscontrato (leggi notato ) problemi".
Disilluso il

0

Qui, lascia che ti presenti altri 2 modi per fare ciò che desideri. Uno è un altro modo per avvolgere std::vector, un altro è il modo di ereditare senza dare agli utenti la possibilità di rompere qualcosa:

  1. Consentitemi di aggiungere un altro modo per avvolgere std::vectorsenza scrivere molti wrapper di funzioni.

#include <utility> // For std:: forward
struct Derived: protected std::vector<T> {
    // Anything...
    using underlying_t = std::vector<T>;

    auto* get_underlying() noexcept
    {
        return static_cast<underlying_t*>(this);
    }
    auto* get_underlying() const noexcept
    {
        return static_cast<underlying_t*>(this);
    }

    template <class Ret, class ...Args>
    auto apply_to_underlying_class(Ret (*underlying_t::member_f)(Args...), Args &&...args)
    {
        return (get_underlying()->*member_f)(std::forward<Args>(args)...);
    }
};
  1. Ereditare da std :: span invece di std::vectored evitare il problema dtor.

0

Si garantisce che questa domanda produca un afferramento di perle senza fiato, ma in realtà non vi è alcuna ragione difendibile per evitare o "moltiplicare inutilmente le entità" per evitare la derivazione da un contenitore standard. L'espressione più semplice, più breve possibile è la più chiara e la migliore.

È necessario esercitare tutte le solite cure attorno a qualsiasi tipo derivato, ma non c'è nulla di speciale nel caso di una base dallo Standard. Sostituire una funzione di un membro di base potrebbe essere complicato, ma non sarebbe saggio avere a che fare con qualsiasi base non virtuale, quindi qui non c'è molto di speciale. Se dovessi aggiungere un membro dati, dovrai preoccuparti di tagliare se il membro dovesse essere mantenuto coerente con il contenuto della base, ma questo è lo stesso per qualsiasi base.

Il posto in cui ho trovato che deriva da un contenitore standard particolarmente utile è quello di aggiungere un singolo costruttore che esegue esattamente l'inizializzazione necessaria, senza possibilità di confusione o dirottamento da parte di altri costruttori. (Ti guardo, costruttori di inizializzazione_elenco!) Quindi, puoi liberamente usare l'oggetto risultante, diviso - passalo facendo riferimento a qualcosa che si aspetta la base, spostalo da un'istanza della base, che cosa hai. Non ci sono casi limite di cui preoccuparsi, a meno che non ti disturbi a legare un argomento modello alla classe derivata.

Un posto in cui questa tecnica sarà immediatamente utile in C ++ 20 è la prenotazione. Dove avremmo potuto scrivere

  std::vector<T> names; names.reserve(1000);

possiamo dire

  template<typename C> 
  struct reserve_in : C { 
    reserve_in(std::size_t n) { this->reserve(n); }
  };

e poi hanno, anche come membri della classe,

  . . .
  reserve_in<std::vector<T>> taken_names{1000};  // 1
  std::vector<T> given_names{reserve_in<std::vector<T>>{1000}}; // 2
  . . .

(in base alle preferenze) e non è necessario scrivere un costruttore solo per chiamare reserve () su di essi.

(Il motivo che reserve_in, tecnicamente, deve attendere C ++ 20 è che gli Standard precedenti non richiedono che la capacità di un vettore vuoto sia preservata attraverso le mosse. Questo è riconosciuto come una svista e si può ragionevolmente prevedere che sarà risolto come un difetto nel tempo per il '20. Possiamo anche aspettarci che la correzione sia, in effetti, retrodatata agli standard precedenti, perché tutte le implementazioni esistenti effettivamente preservano la capacità attraverso i movimenti; gli standard non l'hanno richiesto. la pistola - riservare è quasi sempre solo un'ottimizzazione.)

Alcuni sosterrebbero che il caso di reserve_inè meglio servito da un modello di funzione gratuito:

  template<typename C> 
  auto reserve_in(std::size_t n) { C c; c.reserve(n); return c; }

Un'alternativa di questo tipo è certamente praticabile e, a volte, potrebbe persino essere infinitamente più veloce, grazie a * RVO. Ma la scelta della derivazione o della funzione libera dovrebbe essere fatta per i suoi meriti, e non dalla superstizione infondata (eh!) Sulla derivazione da componenti standard. Nell'esempio di cui sopra, solo la seconda forma funzionerebbe con la funzione libera; anche se al di fuori del contesto di classe, potrebbe essere scritto in modo un po 'più conciso:

  auto given_names{reserve_in<std::vector<T>>(1000)}; // 2
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.