Crear una biblioteca para anular el operador * () del iterador: puntero colgante de riesgo

Estoy tratando de crear el mío.boost::adaptors::transformed.

Aquí está el relacionadocódigo de impulso.

Aquí está su uso (modificado deuna respuesta SO de LogicStuff): -

C funcPointer(B& b){ 
    //"funcPointer" is function convert from "B" to "C"
    return instance-of-C
}

MyArray<B> test;  //<-- any type, must already have begin() & end()

for(C c : test | boost::adaptor::transformed(funcPointer)) {
    //... something ....
}

El resultado será el mismo que: -

for(auto b : test) {
    C c = funcPointer(b);
    //... something ...
}
Mi intento

yo creéCollectAdapter con el objetivo de trabajar comoboost::adaptor::transformed.
Funciona bien en la mayoría de los casos comunes.

Aquí está el completomanifestación yapoyo. (igual que el siguiente código)

La parte problemática esCollectAdapter - El núcleo de mi biblioteca.
No sé si debería almacenar en cachécollection_ por puntero opor valor.

CollectAdapter encapsula subyacentecollection_ (por ejemplo, puntero astd::vector<>): -

template<class COLLECTION,class ADAPTER>class CollectAdapter{
    using CollectAdapterT=CollectAdapter<COLLECTION,ADAPTER>;
    COLLECTION* collection_;    //<---- #1  problem? should cache by value?
    ADAPTER adapter_;           //<---- = func1 (or func2)
    public: CollectAdapter(COLLECTION& collection,ADAPTER adapter){
        collection_=&collection;
        adapter_=adapter;
    }
    public: auto begin(){
        return IteratorAdapter<
            decltype(std::declval<COLLECTION>().begin()),
            decltype(adapter_)>
            (collection_->begin(),adapter_);
    }
    public: auto end(){ ..... }
};

IteratorAdapter (utilizado anteriormente) encapsula el iterador subyacente, cambia el comportamiento deoperator* : -

template<class ITERATORT,class ADAPTER>class IteratorAdapter : public ITERATORT {
    ADAPTER adapter_;
    public: IteratorAdapter(ITERATORT underlying,ADAPTER adapter) :
        ITERATORT(underlying),
        adapter_(adapter)
    {   }
    public: auto operator*(){
        return adapter_(ITERATORT::operator*());
    }
};

CollectAdapterWidget (utilizado a continuación) es solo una clase auxiliar para construirCollectAdapter-ejemplo.

Se puede usar como: -

int func1(int i){   return i+10;   }
int main(){
    std::vector<int> test; test.push_back(5);
    for(auto b:CollectAdapterWidget::createAdapter(test,func1)){
        //^ create "CollectAdapter<std::vector<int>,func1>" instance
         //here, b=5+10=15
    }
}  
Problema

El código anterior funciona bien en la mayoría de los casos, excepto cuandoCOLLECTION Es un objeto temporal.

Más específicamente, el puntero colgante ocurre potencialmente cuando creoadaptador de adaptador de adaptador ....

int func1(int i){   return i+10;    }
int func2(int i){   return i+100;   }
template<class T> auto utilityAdapter(const T& t){
    auto adapter1=CollectAdapterWidget::createAdapter(t,func1);
    auto adapter12=CollectAdapterWidget::createAdapter(adapter1,func2);
    //"adapter12.collection_" point to "adapter1"
    return adapter12;
    //end of scope, "adapter1" is deleted
    //"adapter12.collection_" will be dangling pointer
}
int main(){
    std::vector<int> test;
    test.push_back(5);
    for(auto b:utilityAdapter(test)){
        std::cout<< b<<std::endl;   //should 5+10+100 = 115
    }
}

Esto provocará un error de tiempo de ejecución. Aquí estála demostración de puntero colgante.

En el uso real, si la interfaz es más impresionante, p. utilizar| operador, el error será aún más difícil de detectar: -

//inside "utilityAdapter(t)"
return t|func1;        //OK!
return t|func1|func2;  //dangling pointer
Pregunta

Cómo mejorar mi biblioteca para corregir este error mientras mantengoactuación & robustez & mantenimiento cerca del mismo nivel?

En otras palabras, cómo almacenar en caché los datos o el puntero deCOLLECTION (eso puede seradaptador oestructura de datos real) elegantemente?

Alternativamente, si es más fácil responder codificando desde cero (que modificando mi código), hágalo. :)

Mis soluciones

El código actual almacena en cachépor puntero.
La idea principal de las soluciones es almacenar en cachépor valor en lugar.

Solución 1 (siempre "por valor")

Dejaradaptador cachear elvalor deCOLLECTION.
Aquí está el cambio principal: -

COLLECTION collection_;    //<------ #1 
//changed from   .... COLLECTION* collection_;

Desventaja:-

Estructura de datos completa (p. Ej.std::vector) se copiará en valor: recursos de residuos.
(cuando se usa parastd::vector directamente)Solución 2 (dos versiones de la biblioteca, ¿la mejor?)

Crearé 2 versiones de la biblioteca:AdapterValue yAdapterPointer.
Tengo que crear clases relacionadas (Widget,AdapterIterator, etc.) también.

AdapterValue - por valor. (diseñado parautilityAdapter())AdapterPointer - por puntero. (diseñado parastd::vector)

Desventaja:-

Código duplicado mucho = bajo mantenimientoLos usuarios (codificadores) deben ser muy conscientes sobre cuál elegir = baja robustezSolución 3 (tipo de detección)

Puedo usar una especialización de plantilla que haga esto: -

If( COLLECTION is an "CollectAdapter" ){ by value }  
Else{ by pointer }    

Desventaja:-

No coopera bien entre muchas clases de adaptadores.
Tienen que reconocerse mutuamente:Reconocido = debe cachearpor valor.

Perdón por una publicación muy larga.

Respuestas a la pregunta(1)

Su respuesta a la pregunta