2011-10-19 16 views
9

fornisce insert_after e erase_after membri a cui potrebbe non essere necessario accedere effettivamente all'oggetto std::forward_list. Pertanto possono essere implementate come funzioni membro static e essere chiamate senza un oggetto elenco, utile per un oggetto che desidera eliminare se stesso da un elenco, che è un uso molto comune. EDIT: questa ottimizzazione si applica solo alle specializzazioni forward_list su std::allocator o agli allocatori stateless definiti dall'utente.I membri std :: forward_list possono essere implementati come statici?

È possibile eseguire un'implementazione conforme standard?

§17.6.5.5/3 dice

A call to a member function signature described in the C++ standard library behaves as if the implementation declares no additional member function signatures.

con una nota in calce

A valid C++ program always calls the expected library member function, or one with equivalent behavior. An implementation may also define additional member functions that would otherwise not be called by a valid C++ program.

Non è chiaro per me se l'aggiunta di static creerebbe una funzione di membro "diverso", ma la rimozione di un (implicito argomento non dovrebbe rompere nulla che l'aggiunta di argomenti predefiniti non sarebbe, e che è legale. (Non è possibile legalmente portare un PTMF a qualsiasi funzione membro standard.)

Mi sembra che la libreria debba essere autorizzata a farlo, ma non sono sicuro che alcune regole potrebbero essere violate. E quanto sono normativi i prototipi della funzione membro elencati?

+3

Le operazioni di lista di muting richiedono l'accesso all'allocatore della lista, quindi dubito che potrebbero essere statiche (specialmente con i nuovi allocatori di stato). –

+0

Ancora, il modello può essere specializzato per il caso estremamente comune di 'std :: allocator' e anche dall'utente per se stesso, se lo desidera. – Potatoswatter

+0

Come ti specializzeresti in base alla conoscenza del * iteratore * da solo? L'iteratore non sa a quale elenco appartiene, né quale allocatore viene utilizzato da quell'elenco. –

risposta

9

Lo standard dice che puoi farcela se nessuno può dire la differenza. E tu hai ragione sul fatto che non è possibile creare legalmente un PTMF in forward_list, quindi sei sicuro in questo modo.

Il pericolo di allocatori personalizzati è già stato segnalato. Ma anche per std::allocator<T> c'è il pericolo che qualcuno possa specializzarsi su std::allocator<MyType> e quindi rilevare che non è stato chiamato il .

Va bene, ma si può dire specializzarsi std::forward_list<int> (senza allocatore personalizzato, nessun utente value_type definito) e fare insert_after statica?

No. Questa modifica potrebbe essere rilevata con le nuove funzionalità di SFINAE. Ecco una demo:

#include <memory> 
#include <iostream> 

template <class T, class A = std::allocator<T>> 
class forward_list 
{ 
public: 
    typedef T value_type; 
    struct const_iterator {}; 
    struct iterator {}; 

    iterator insert_after(const_iterator p, const T& x); 
}; 

template <class C> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    -> decltype(C::insert_after(p, x)) 
{ 
    std::cout << "static\n"; 
    return typename C::iterator(); 
} 

template <class C> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    -> decltype(c.insert_after(p, x)) 
{ 
    std::cout << "not static\n"; 
    return typename C::iterator(); 
} 

int main() 
{ 
    ::forward_list<int> c; 
    test(c, ::forward_list<int>::const_iterator(), 0); 
} 

Questo programma viene eseguito e stampa:

not static 

Ma se faccio insert_after statica:

static iterator insert_after(const_iterator p, const T& x); 

tanto sono un errore di compilazione:

test.cpp:34:5: error: call to 'test' is ambiguous 
    test(c, ::forward_list<int>::const_iterator(), 0); 
    ^~~~ 
test.cpp:16:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >] 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    ^
test.cpp:24:6: note: candidate function [with C = forward_list<int, std::__1::allocator<int> >] 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x) 
    ^
1 error generated. 

Rilevata differenza.

Pertanto non è conforme a forward_list::insert_after statico.

Aggiornamento

Se si vuole fare il sovraccarico "statica" callable, è sufficiente per renderlo un po 'più desiderabile che il sovraccarico "non statico".Un modo di fare che sta cambiando il sovraccarico "non statico" a:

template <class C, class ...Args> 
auto test(C& c, typename C::const_iterator p, const typename C::value_type& x, Args...) 
    -> decltype(c.insert_after(p, x)) 
{ 
    std::cout << "not static\n"; 
    return typename C::iterator(); 
} 

Ora il test stamperà o "statica" o "non statico" a seconda se la funzione insert_after membro è statico o no.

+0

Non avevo pensato a 'std :: allocator', specialista per l'utente, ottimo punto. Ma il '' statico '' non è sovraccarico di un modello con nessuna specializzazione ben formata, quindi mal formata senza necessità di diagnostica? – Potatoswatter

+0

Risposta aggiornata con "rilevatore statico" completamente funzionante. –

+0

Ok ... non essere difficile, ma in che modo è diverso da, ad esempio, rilevare un argomento predefinito trovando il tipo di una funzione membro non sovraccaricata? Ora il modello produce una specializzazione valida, ma sembra ancora essere UB perché l'espressione 'C :: insert_after()' non è definita dallo standard. Allo stesso modo, è un'implementazione variadic basata su modelli di Allocator :: construct' non conforme in C++ 03 perché può essere chiamata come 'construct <> (...)'? Questo è davvero il punto della domanda iniziale, per quanto riguarda la profondità delle specifiche della libreria. – Potatoswatter

Problemi correlati