Newsgroup: it.comp.lang.c++
Titolo: constantness (generalizzazione)

Generalizziamo il problema esposto nel post "temporary constantness".
Sia f una funzione che riceve un oggetto di tipo X e restituisce un 
oggetto di tipo Y:
   Y f(X),
dove X e Y possono essere di tipo A o B e le funzioni di conversione 
A->B e B->A sono note.
La conversione A->B è implicita poiché sicura, mentre quella B->A è 
insicura.
Il comportamento di f è illustrato nel seguente schema:
Caller   f    Caller
   A     A->B -> A
   B      B      B
La funzione f non può accedere a un oggetto di tipo B, quindi, se il 
chiamante passa un oggetto di tipo A, questo viene convertito nel tipo B 
in modo che 'f' possa lavorarci, e poi l'oggetto restituito viene 
riconvertito nel tipo A. La conversione B->A è lecita solo se non 
fornisce al chiamante privilegi che non possedeva prima di chiamare 'f'. 
Questo significa che tale conversione è lecita qualora l'oggetto 
restituito sia l'originale (passato per indirizzo) o una copia (o una 
copia di una copia) dell'oggetto originale.
Come realizzereste una simile cosa?
La soluzione dovrebbe essere generale e utilizzabile da un utente 
qualsiasi col mimimo sforzo possibile.
Kiuhnm

 copia di una copia) dell'oggetto originale.
Ecco un tentativo, non privo di problemi.
Ciao,
Nicola
// Rappresenta la relazione tra A e B e fornisce la conversione
"pericolosa".
// Nella forma base e' chiaramente inutile
template <typename A>
struct BfromA {
    typedef int B;
    A operator() (B const & b);
};
// Gestisce la conversione verso il tipo "di servizio" e viceversa
template <typename A, typename B = typename BfromA<A>::B>
class wrapper;
// Gestisce il caso di coincidenza tra tipo base e tipo di servizio
template <typename BB>
class wrapper<BB, BB> {
public:
    typedef BB B;
    wrapper(B const & bb, BfromA<B> = BfromA<B>()) : b(bb) {}
    operator B&() { return b; }
protected:
    B b;
};
template <typename A, typename BB>
class wrapper : public wrapper<BB, BB> {
public:
    typedef BB B;
    wrapper(A const & aa, BfromA<A> c = BfromA<A>()) :
            wrapper<B, B>(aa), conv(c)
    {
    }
    operator A () { return conv(this->b); }
private:
    BfromA<A> conv;
};
// Un esempio di utilizzo. La massima genericita' costa in termini di
chiarezza;
// Applicazioni piu' specifiche dovrebbero risultare piu' semplici
template <typename T>
T f(T const & t, BfromA<T> c = BfromA<T>()) {
    wrapper<T> w(t, c);
    typename wrapper<T>::B & pb = w;
    ++pb;
    return w;
}
// Una prima applicazione: puntatore e puntatore a const
template <typename A>
struct BfromA<A *> {
    typedef A const * B;
    A * operator() (B const & b) { return const_cast<A *>(b); }
};
template <typename A>
struct BfromA<A const *> {
    typedef A const * B;
};
int main() {
    int a[] = { 1, 2, 3 };
    int * i = a;
    int * j = f(i);
    int const * k = a;
    int const * l = f(k);
    return *l;
}
// L'applicazione agli iteratori comporta notevoli difficolta'
// di generalizzazione: si incoccia sempre o nella non deducibilita'
// della forma A<B>::C o nella difficolta' di specificare i
// contenitori standard come argomenti di template a causa dei loro
// parametri facoltativi. In piu' questo esempio specifico
// interferisce col precedente in quelle implementazioni che usano i
// puntatori come iteratori di std::vector
#include <iterator>
#include <vector>
template <>
struct BfromA<std::vector<int>::iterator> {
    typedef std::vector<int>::iterator A;
    typedef std::vector<int>::const_iterator B;
    BfromA(A beg) : begin(beg) {}
    A operator() (B const & b) {
        A a(begin);
        std::advance(a, std::distance(B(begin), b));
        return a;
    }
    A begin;
};
int main() {
    int a[] = { 1, 2, 3 };
    std::vector<int> v(a, a+2);
    std::vector<int>::iterator vi = v.begin();
    std::vector<int>::iterator vj = f(vi,
BfromA<std::vector<int>::iterator>(vi));
    return *vj;
}

Nicola Musatti wrote:
[...]
Finalmente ho trovato il tempo di esaminare il tuo codice!
Direi che hai fatto un buon lavoro, però ci sono alcuni problemi, come 
da te premesso, che non penso si possano eliminare con quell'approccio.
Il problema principale è qui:
     template <typename T>
     T f(T const & t, BfromA<T> c = BfromA<T>()) {
         wrapper<T> w(t, c);
         typename wrapper<T>::B & pb = w;
         ++pb;
         return w;
     }
L'interfaccia di 'f' ne risente e, soprattutto, la "protezione" non è 
completa. Per es. è possibile avere
     template <typename T>
     T f(T const & t, BfromA<T> c = BfromA<T>()) {
         wrapper<T> w(t, c);
         typename wrapper<T>::B & pb = w;
         ++pb;
	T p = w;	// <--
	*p = 10;	// <--
         return w;
     }
oppure
     template <typename T>
     T f(T const & t, BfromA<T> c = BfromA<T>()) {
         wrapper<T> w(t, c);
         typename wrapper<T>::B & pb = w;
         ++pb;
	*t = 10;	// <--
         return w;
     }
senza tanti complimenti.
Come ho già detto, non credo si possa fare di meglio usando questo 
approccio (imposto da me nel post di apertura).
Grazie per esserti cimentato. Non si poteva fare di meglio.
Ho passato alcune ore a ripensare al problema originale (ricorderai il 
post da cui è nato tutto) e penso di aver trovato qualcosa di soddisfacente.
Visto che si tratta di un cambio di rotta, preferisco aprire un nuovo 
thread.
Kiuhnm