2015-07-26 13 views
10

A volte ho bisogno delle istanze shared_ptr che hanno un deleter no-op, perché un'API si aspetta un'istanza shared_ptr che desidera archiviare per un tempo limitato, ma mi viene fornito un puntatore raw che non sono autorizzato a possedere per un tempo maggiore di per cosa sto correndo.È un aliasing vuoto shared_ptr una buona alternativa a un no-op che elimina shared_ptr?

Per questo caso, ho utilizzato un deleter no-op, come ad esempio [](const void *){}, ma oggi ho scoperto che c'è un'altra alternativa a quella, usando (o abusando?) Il aliasing constructor di shared_ptr:

void f(ExpectedClass *ec) { 
    std::shared_ptr<ExpectedClass> p(std::shared_ptr<void>(), ec); 
    assert(p.use_count() == 0 && p.get() != nullptr); 
    apiCall(p); 
} 

La mia domanda è, qual è il modo migliore per farlo e perché? Le aspettative di rendimento sono le stesse? Con un deleter no-op mi aspetto di pagare un costo per la memorizzazione del deleter e del conteggio dei riferimenti, che non sembra essere il caso quando si utilizza il costruttore aliasing con lo shared_ptr vuoto.

+2

Sì, c'è una nota che menziona questa stranezza in [util.smartptr.shared.const]/16. IMHO è il corollario del fatto che puoi avere un 'shared_ptr' non vuoto che memorizza null (' shared_ptr ((T *) nullptr) '). Potenzialmente un po 'confuso, ma valido. Un problema con questo uso è che le copie di 'p' non condividono la proprietà con esso, perché nessuno di loro possiede nulla. Ciò potrebbe essere importante per alcuni usi che prevedono il controllo degli oggetti che condividono la proprietà (tramite 'owner_less') ma probabilmente non è un problema per l'utilizzo che descrivi. –

+0

@ JonathanWakely grazie per la nota. La nota prima di esso/15) mi confonde. Perché l'utente deve assicurarsi che 'p' rimanga valido finché il gruppo di proprietà di' r' non viene distrutto in 'shared_ptr <> (r, p)'? Pensavo che fosse il lavoro di 'shared_ptr'. Se lo metto via, il puntatore aliasing sarà sempre valido perché impedisce al gruppo di proprietà di 'r' di essere distrutto, in modo che l'utente possa" copiarlo e dimenticarlo "o mi manchi qualcosa? –

+1

Ah capisco cosa significa. Nel caso in cui la durata di 'p' non sia legata alla durata di' r' (che è consentita), allora la durata di 'r' non implica la durata di' p' e quindi l'utente deve garantirlo. –

risposta

6

quanto riguarda le prestazioni, il seguente parametro di riferimento mostra figure irregolari:

#include <chrono> 
#include <iostream> 
#include <limits> 
#include <memory> 

template <typename... Args> 
auto test(Args&&... args) { 
    using clock = std::chrono::high_resolution_clock; 
    auto best = clock::duration::max(); 

    for (int outer = 1; outer < 10000; ++outer) { 
     auto now = clock::now(); 

     for (int inner = 1; inner < 20000; ++inner) 
      std::shared_ptr<int> sh(std::forward<Args>(args)...); 

     auto time = clock::now()-now; 
     if (time < best) { 
      best = time; 
      outer = 1; 
     } 
    } 

    return best.count(); 
} 

int main() 
{ 
    int j; 

    std::cout << "With aliasing ctor: " << test(std::shared_ptr<void>(), &j) << '\n' 
       << "With empty deleter: " << test(&j, [] (auto) {}); 
} 

uscita sulla mia macchina con clang++ -march=native -O2:

With aliasing ctor: 11812 
With empty deleter: 651502 

GCC con opzioni identiche dà un rapporto ancora più grande, 5921: 465.794.
E Clang con -stdlib=libc++ produce un enorme 12: 613175.

+0

Infatti con libC++, le parti shared_ptr sono completamente ottimizzate quando si utilizza l'hack aliasing: http://coliru.stacked-crooked.com/a/84b6d94a03d180b5 –

+0

@ JohannesSchaub-litb ... Qual è il linguaggio che emette? – Columbo

+0

è la lingua intermedia (LLVM IR) prima della conversione in assembly x86.Trovo più facile craspare, specialmente i loop, dato che è ancora in formato SSA. –

Problemi correlati