2013-03-04 14 views
5

Questa domanda è arrivata come ho risposto this question: lo standard consente e fornisce garanzie circa friend -ing classi e/o funzioni di libreria standard?Classi friending definite nel namespace std: eventuali garanzie?

In questo caso particolare, la situazione la questione era se:

class MyUserDefinedType 
{ 
    friend struct std::default_delete<MyUserDefinedType>; 

private: 
    ~MyUserDefinedType() { } 
} 

è garantita per consentire MyUserDefinedType da immagazzinare in un oggetto std::unique_ptr<MyUserDefinedType> o std::shared_ptr<MyUserDefinedType> con il deleter predefinito.

In generale, le classi sono descritte nella libreria standard richiesta per implementare direttamente le loro funzionalità oppure possono utilizzare qualsiasi livello arbitrario di riferimento indiretto? Ad esempio, è possibile che

  • std::default_delete<MyUserDefinedType> è in realtà un using alias di una classe definita in un namespace interna std, nel qual caso la dichiarazione friend sarebbe illegale

o

  • std::default_delete<MyUserDefinedType> chiama un'altra classe che esegue effettivamente l'eliminazione, nel qual caso la dichiarazione friend non avrebbe l'ef desiderato fect

o qualcos'altro su queste linee?

La mia ipotesi è che questo è UB non garantito per funzionare, ma sono curioso se questo è indirizzato in particolare dallo standard.

Questo specifico esempio citato sopra funziona per tronco clang (w/libC++) e GCC 4.7.2 (w/libstdC++), FWIW

+0

Non conosco la risposta, ma "fare amicizia" 'std :: default_delete <>' sarà, se funziona, consentire a quasi chiunque di chiamare il distruttore della classe. Mi chiedo perché rendere privato il distruttore in primo luogo se poi viene usata una dichiarazione di amico che la rende (quasi) pubblica di nuovo. (Certo, vorrebbe comunque dire che gli oggetti di archiviazione automatica non possono essere disallocati.) – jogojapan

+0

Non mi sarei aspettato che fosse vero (anche se ho smesso di essere un esperto di C++ una decina di anni fa); sperimentalmente sembra non essere vero per gli ultimi g ++ e vC++, FWIW. –

+0

non pensavo che la soluzione fosse buona, ho suggerito di implementare un nuovo deleter –

risposta

5

è possibile che std::default_delete<MyUserDefinedType> è in realtà un utilizzando alias di una classe definita in un namespace interno di std, nel qual caso la dichiarazione di amicizia sarebbe illegale?

No. Al comma 20.7.1.1.2 del C++ 11 standard:

namespace std { 
    template <class T> struct default_delete { 
     constexpr default_delete() noexcept = default; 
     template <class U> default_delete(const default_delete<U>&) noexcept; 
     void operator()(T*) const; 
    }; 
} 

Il fatto che deve essere un modello di classe viene specificato in modo esplicito. Ciò significa che non può essere un modello alias. Se così fosse, sarebbe anche impossibile specializzarlo.

è possibile che std::default_delete<MyUserDefinedType> chiami qualche altra classe che esegue effettivamente l'eliminazione, nel qual caso la dichiarazione di amicizia non avrebbe l'effetto desiderato?

Sì. Niente nello standard specifica che la chiamata non può essere eseguita da qualche helper interno. Per paragrafo 20.1.1.2:

void operator()(T *ptr) const; 

3 Effetti: chiamate delete su ptr.

4 Note: Se T è un tipo incompleto, il programma è mal formato.

Questa specifica solo ciò che il effetto di invocare l'operatore chiamata sulla default_delete<> funtore dovrebbe essere, non come ciò è perseguito concretamente (direttamente all'interno del corpo dell'operatore chiamata o delegando la compito a qualche funzione membro di qualche altra classe).

+0

la risposta per la seconda parte ha senso per me, ma sai se questo problema è indirizzato in modo specifico da qualche parte? è esplicitamente UB ad essere amico di una classe standard o solo implicitamente? –

+0

@StephenLin: Perché dovrebbe essere UB? Non è una soluzione portatile, credo, perché la chiamata a 'delete' * potrebbe *, ma non deve, essere eseguita dalla classe' default_delete' stessa. –

+0

beh, ok, voglio dire, è un comportamento che cambia la semantica del programma (tale che può o non può essere compilato) e non è definito ... forse UB è troppo forte, ma che altro sarebbe? non specificato? –

1

In generale, le classi sono descritte nella libreria standard richiesta per implementare direttamente le loro funzionalità oppure possono utilizzare qualsiasi livello di riferimento indiretto arbitrario?

In generale un'implementazione può indirettamente per quanto desidera. Ad esempio dare uno sguardo alle implementazioni dei contenitori standard e ai loro iteratori, o semplicemente usarli male e vedere quali modelli sono coinvolti dai messaggi di errore. Tuttavia, dal momento che lo default_delete non è niente di magico, che dovrebbe essere un one-liner, puoi aspettarti che faccia il lavoro stesso, ma non è garantito.

La mia ipotesi è che questo sia UB ma sono curioso se questo è indirizzato in modo specifico dallo standard.

Non è UB, è solo non specificato.

Si potrebbe essere sicuri se si è appena specializzato default_delete<MyUserDefinedType>è concesso di specializzare modelli di libraray standard), ma non lo farei.

Non vorrei usare l'amicizia, specialmente se si tratta di modelli non specializzati. Considerate questo:

//your code 
class MyUserDefinedType 
{ 
    friend struct std::default_delete<MyUserDefinedType>; //for deletion 
private: 
    int veryPrivateData; 
    ~MyUserDefinedType() { } 
}; 

//evil colleague's code: 
namespace std { 
    //we may specialize std-templates for UDTs... 
    template<> 
    struct default_delete<MyUserDefinedType> 
    { 
    constexpr default_delete() noexcept = default; 
    template <class U> default_delete(const default_delete<U>&) noexcept {} 
    void operator()(T* pt) const { delete pt; } 

    //sneaky... 
    void access(MyUserDefinedType& mudt, int i) const 
    { mudt.veryPrivateData = i; } 
    }; 
} 

void somewhere_deep_in_the_code() 
{ 
    MyUserDefinedType& myUDT = /*something...*/; 
    std::default_delete<MyUserDefinedType>().access(myUDT, 42); //tricked you! 
} 

amici possono fare qualsiasi cosa a voi. Sceglili con cautela. In questo caso, consiglierei davvero un deleter personalizzato, presupponendo che abbia senso rendere privato il distruttore ma fornirne l'accesso tramite il deleter.

+0

yeah ho suggerito un deleter personalizzato, questo è n o mia soluzione raccomandata –

+0

+1 grazie per l'input, spero comunque che qualcuno possa trovare una citazione molto autorevole su classi/funzioni standard di 'friend'ing –

Problemi correlati