2012-04-22 22 views
6

Una clase contiene un std::vector<int*>. El código externo necesita acceso de solo lectura a este vector, no debería poder modificar los contenidos (ni los punteros ni sus contenidos). Dentro de la clase, los valores puede cambio (por ejemplo double_values(), y así almacenarlas como std::vector<const int*> no es posible.Trate el vector <int*> como vector <const int*> sin copiar (C++ 0x)

¿Hay una manera de devolver el std::vector<int*> como std::vector<const int*> sin hacer una copia? Es mucho que tendría ser, porque const es simplemente operando en tiempo de compilación para decir lo que puede y no puede ser modificado

Código:. (compilar con g++ -std=c++0x)

class ReadOnlyAccess 
{ 
public: 
    ReadOnlyAccess(const std::vector<int*> & int_ptrs_param): 
    int_ptrs(int_ptrs_param) 
    { 
    } 
    const std::vector<int*> & get_int_ptrs() const 
    { 
    return int_ptrs; 
    } 
    std::vector<const int*> safely_get_int_ptrs() const 
    { 
    // will not compile (too bad): 
    // return int_ptrs; 

    // need to copy entire vector 
    std::vector<const int*> result(int_ptrs.size()); 
    for (int k=0; k<int_ptrs.size(); k++) 
     result[k] = int_ptrs[k]; 
    return result; 
    } 
    void double_values() 
    { 
    for (int*p : int_ptrs) 
     *p *= 2; 
    } 
    void print() const 
    { 
    for (const int * p : int_ptrs) 
     std::cout << *p << " "; 
    std::cout << std::endl; 
    } 
private: 
    std::vector<int*> int_ptrs; 
}; 

int main() { 
    ReadOnlyAccess roa(std::vector<int*>{new int(10), new int(20), new int(100)}); 
    std::vector<const int*> safe_int_ptrs = roa.safely_get_int_ptrs(); 
    // does not compile (good) 
    // *safe_int_ptrs[0] = -100000; 
    roa.print(); 

    const std::vector<int*> & int_ptrs = roa.get_int_ptrs(); 
    // changes are made to the internal class values via the accessor! nooooo! 
    *int_ptrs[0] = -100000; 
    roa.print(); 

    return 0; 
} 
+0

posible duplicado de [vector y const] (http://stackoverflow.com/questions/2102244/vector-and-const) –

+0

ver http://stackoverflow.com/questions/2868485/cast-vectort-to- vectorconst-t – WeaselFox

+2

@Bo: Esa pregunta no responde a esta, realmente. Y WeaselFox 'también es irrelevante debido a la indirección del puntero. – Xeo

Respuesta

4

Retu rning el vector implicará una copia si desea mantener los punteros const de todos modos.

Sin embargo, si su objetivo es proporcionar una forma de usar los valores sin modificarlos o modificar su contenedor, un algoritmo basado en un patrón de visitante podría ser una muy buena solución, en particular ahora que podemos usar expresiones lambda:

#include <vector> 
#include <iostream> 

class Data 
{ 
public: 

    //...whatever needed to fill the values 

    // here we assume that Func is equivalent to std::function< void (int)> or std::function< void (const int&) > and can return anything that will be ignored here. 
    template< class Func > 
    void for_each_value(Func func) const // read-only 
    { 
     for(const int* value : m_values) // implicit conversion 
     { 
      func(*value); // read-only reference (const &), or copy 
      // if func needs to work with the adress of the object, it still can by getting a reference to it and using & to get it's adress 
     } 
    } 


    void print() const 
    { 
     std::cout << "\nData values: \n"; 
     for_each_value([](const int value) { std::cout << " "<< value << '\n'; }); 
    } 

    void count_values() const { return m_values.size(); } 

private: 

    std::vector<int*> m_values; 

}; 



int main() 
{ 
    Data data; 
    // ... whatever needed to fill the data 

    data.print();  

    std::vector<int> modified_values; 
    data.for_each_value([&](int value) { modified_values.push_back(value + 42); }); 

    return 0; 
} 

Si usted entiende eso, y las diferentes formas de utilizar los valores pueden reducirse a unos algoritmos media genéricos, a continuación, que hará que su código más simple y permitirá mantener los datos dentro de sus estructuras en lugar de exponer son las agallas

+0

Usted sabe sobre 'std :: for_each', ¿verdad? – jalf

+0

Oviously. ¿Por qué preguntas exactamente? Aquí std :: for_each usado fuera de la clase no ayudaría, ya que sería necesario exponer el iterador del contenedor real, o hacer una copia, que se evitará mediante una pregunta. Podría haber reemplazado el ciclo en for_each_value por std :: for_each, sin ningún beneficio. El punto de mi respuesta es que no hay una copia del contenedor, y sea cual sea el algoritmo de recorrido, no es necesario que expongas el contenido de la clase SI sabes qué tipo de recorrido necesitan los usuarios de la clase. Obviamente, si no sabes, abraza a YAGNI. – Klaim

+0

Dicho esto, algunos casos requieren que la clase exponga el contenido por iteradores. Pero no creo que sea necesario para la pregunta específica. – Klaim

1

Puede proporcionar una vista de los valores de const a través de iteradores personalizados. Una manera fácil sería utilizar boost::iterator:

#include <boost/iterator/indirect_iterator.hpp> 

class ReadOnlyAccess 
{ 
// ... 
    typedef boost::indirect_iterator<const int* const*, const int> const_val_iter_type; 
    const_val_iter_type cval_begin() { 
     return it_t{const_cast<const int* const*>(&int_ptrs[0])}; 
    } 
} 

int main() { 
    // ... 
    auto x = roa.cval_begin(); 
    std::cout << x[0] <<' ' << x[1] << x[2] <<'\n'; 
    // we can still access the pointers themselves via .base() member function: 
    for (int i=0; i<3; ++i) 
     assert(x.base()[i] == safe_int_ptrs[i]); 
    // the values are read-only, the following does not compile: 
    // x[0] = -1; 
    // **x.base() = -1; 
    // *x.base() = nullptr; 
} 

Si usamos boost::indirect_iterator<typename std::vector<int*>::const_iterator, const int> para const_val_iter_type, podríamos modificar los valores de punta a través de .base() (pero no directamente, como en, por ejemplo x[0] = -1), por lo que esta solución no es general.

Cuestiones relacionadas