2012-01-15 15 views
13

C++ 03 consente di qualificare i parametri di funzione come const, volatile e/oi riferimenti di lvalue (&).C++ 11: Riassunto su puntatori funzione const, riferimento volatile, di riferimento lvalue e riferimento a rvalore?

C++ 11 aggiunge uno di più: riferimenti di rvalue (&&).

Inoltre, C++ consente di sovraccaricare le funzioni in base ai qualificatori dei loro parametri, in modo che venga selezionato il sovraccarico più appropriato quando si chiama la funzione.

Una funzione membro può essere pensata concettualmente come una funzione che accetta un parametro aggiuntivo, il cui tipo è un riferimento a un'istanza della classe di cui è membro. È possibile sovraccaricare una funzione membro in base ai qualificatori di questo 'parametro aggiuntivo' più o meno allo stesso modo di qualsiasi altro parametro. Ciò è espresso mettendo qualificazioni alla fine della firma funzione:

struct Foo 
{ 
    int& data();    // return a non-const reference if `this` is non-const 
    const int& data() const; // return a const reference if `this` is const 
}; 

in C++ 03, const e volatile qualificatori sono possibili, e C++ 11 permette anche & e && (& potrebbe teoricamente stato permesso in C++ 03, ma non lo era).

Qualsiasi combinazione di qualificazione può essere utilizzato, con l'eccezione che & e && sono mutuamente esclusivi, che rende per 2^2 = 4 possibilità in C++ 03 e 2^4-4 = 12 a C++ 11 .

Questo può essere un bel dolore quando si desidera lavorare con puntatori a funzione membro, perché non sono anche un po 'polimorfica in queste qualificazioni: le qualificazioni sul "this tipo" di un puntatore a funzione membro passato come l'argomento deve corrispondere esattamente a quelli sul tipo del parametro che viene passato come. Anche il C++ non offre alcuna possibilità esplicita di astrarre i qualificatori. In C++ 03 questo era principalmente OK, perché dovresti scrivere una versione const e una versione non-const e nessuno si preoccupa di volatile, ma nel caso patologico in C++ 11 (che non è così raro come è patologico) potresti dover scrivere manualmente fino a 12 sovraccarichi. Per funzione.

sono stato molto felice di scoprire che se si passa il tipo della classe racchiude come un parametro di template e trarre il tipo di un puntatore a funzione membro da esso, che const e volatile qualificazioni sono ammessi e propagate come ci si aspetterebbe :

template<typename Object> 
struct Bar 
{ 
    typedef int (Object::*Sig)(int); 
}; 

Bar<Baz>;    // Sig will be `int (Baz::*)(int)` 
Bar<const Baz>;   // Sig will be `int (Baz::*)(int) const` 
Bar<volatile Baz>;  // Sig will be `int (Baz::*)(int) volatile` 
Bar<const volatile Baz>; // Sig will be `int (Baz::*)(int) const volatile` 

Questo è molto bello di dover scrivere tutti i casi manualmente.

Sfortunatamente, non sembra funzionare per & e &&.

GCC 4.7 dice:

error: forming pointer to reference type ‘Baz&&’

Ma non è troppo sorprendente, dal momento che GCC come di 4,7 non ha ancora il supporto per le qualificazioni di riferimento su this.

ho anche provato con Clang 3.0, che ha tale supporto:

error: member pointer refers into non-class type 'Baz &&'

Oh, bene.

Sono corretto nel concludere che ciò non è possibile e che non è possibile astrarre su qualificatori di riferimento su "this type" di puntatori funzione membro? Qualsiasi altra tecnica di astrazione su qualificatori (in particolare su this) diversa dal caso specifico quando si passa il "this tipo" come parametro del modello sarebbe anche apprezzata.

(Vale la pena sottolineare che se C++ non distinguesse tra funzioni membro e funzioni normali, tutto sarebbe banale: si userebbe il parametro template come tipo di parametro della funzione (puntatore), e l'argomento del modello sarebbe passato così com'è, i qualificatori intatti, nessun pensiero aggiuntivo necessario.)

+1

Con quale frequenza è necessario un comportamento diverso per tutte le possibili combinazioni di qualificatori? (O, del resto, anche due o tre combinazioni possibili?) –

+1

Se sto provando a scrivere un'astrazione per oggetti simili a funzioni (nello spirito di std :: function ma non è la stessa cosa), allora la completezza è un obiettivo di design . Non posso davvero estrapolare o stimare (quanto a quanto frequentemente) ma so che ci ho già provato prima. – glaebhoerl

risposta

4

Hai mai pensato di specializzare semplicemente il tuo modello?

Si può solo aggiungere le due versioni:

template <typename Object> 
struct Bar<Object&> { 
    typedef int (Object::*Sig)(int)&; 
}; 

template <typename Object> 
struct Bar<Object&&> { 
    typedef int (Object::*Sig)(int)&&; 
}; 

E poi il compilatore sceglierà la specializzazione a destra (o di riserva per il caso generale) in modo appropriato.

Ciò consente di risparmiare dalla cosa const/volatile, ma implica che è necessario scrivere il codice 3 volte.

+0

Sì, il fatto che 'const' e' volatile' siano gestiti in modo intelligente significa che devo scrivere solo tre versioni invece di dodici, il che è bello. – glaebhoerl