C ++ Modelli in cui il tipo è un shared_ptr

1

Quando si creano classi template in C ++, se il tipo su cui il template sarà specializzato è un tipo shared_ptr, è meglio rendere T un tipo shared_ptr o rendere T un tipo non-pointer?

Ad esempio, quale delle seguenti classi è migliore, FirstExample o SecondExample? O in alternativa, è interamente dipendente dal contesto? In tal caso, puoi un esempio di contesto in cui FirstExample è migliore e un contesto in cui SecondExample è migliore?

Nel mio caso, la chiarezza di utilizzo è una priorità più alta delle prestazioni.

#include <iostream>
#include <memory>

using IntPtr = std::shared_ptr<int>;

template <class T>
class FirstExample
{

public:
    FirstExample( std::shared_ptr<T> value )
    :myData( value ) {}
    std::shared_ptr<T> getData() const { return myData; }

private:
    std::shared_ptr<T> myData;
};

template <class T>
class SecondExample
{

public:
    SecondExample( const T& value )
    :myData( value ) {}

    T getData() const { return myData; }

private:
    T myData;
};



int main(int argc, const char * argv[])
{
    FirstExample<int> firstInstance( std::make_shared<int>( 10 ) );
    IntPtr copyOfFirstInstanceData = firstInstance.getData();

    SecondExample<IntPtr> secondInstance( std::make_shared<int>(10) );
    IntPtr copyOfSecondInstanceData = secondInstance.getData();

    return 0;
}
    
posta Matthew James Briggs 12.02.2015 - 23:55
fonte

1 risposta

3

Per prima cosa regoliamo i vettori definiti dall'utente per l'equivalenza:

  1. Utilizza il valore pass-by e la costruzione delle mosse:

    FirstExample( std::shared_ptr<T> value ) : myData(std::move(value)) {}
    SecondExample( T value ) : myData(std::move(value)) {}
    
  2. Utilizza pass-per-riferimento e copia-costruzione:

    FirstExample( const std::shared_ptr<T>& value ) : myData(value) {}
    SecondExample( const T& value ) : myData(value) {}
    
  3. Utilizza pass-per-riferimento e move- / copy-construction:

    FirstExample( const std::shared_ptr<T>& value ) : myData(value) {}
    SecondExample( const T& value ) : myData(value) {}
    FirstExample( std::shared_ptr<T>&& value ) : myData(std::move(value)) {}
    SecondExample( T&& value ) : myData(std::move(value)) {}
    

Per inciso, cambierei anche getData , per evitare di copyng dove non necessario:

const T& getData() const { return myData; }
const std::shared_ptr<T>& getData() const { return myData; }

Ora, ThirdExample si comporta come FirstExample :

template<class T> using ThirdExample = SecondExample<std::shared_ptr<T>>;

Il vantaggio di usare il primo è meno digitando e possibilmente migliori messaggi di errore.
Il vantaggio di utilizzare il secondo è maggiore flessibilità, in quanto è possibile utilizzare un altro puntatore intelligente, purché offra l'interfaccia corretta.

Ma perché dovresti decidere tra uno o l'altro? Come dimostra ThirdExample , puoi implementare il secondo esempio e fornire il primo come semplice modello alias per comodità, proprio come fa la libreria standard per std::integer_sequence e std::index_sequence .

    
risposta data 13.02.2015 - 00:45
fonte

Leggi altre domande sui tag