ejemplo - list c++
Cómo eliminar la constness de const_iterator? (9)
Creo que esta conversión no es necesaria en un buen programa diseñado.
Si necesita hacer esto, intente rediseñar el código.
Como solución alternativa puede hacer lo siguiente:
typedef std::vector< size_t > container_type;
container_type v;
// filling container code
container_type::const_iterator ci = v.begin() + 3; // set some value
container_type::iterator i = v.begin();
std::advance( i, std::distance< container_type::const_iterator >( v.begin(), ci ) );
Pero creo que a veces esta conversión es imposible porque tus algoritmos pueden no tener acceso al contenedor.
Como una extensión de esta pregunta, ¿los const_iterators
son más rápidos? , Tengo otra pregunta sobre const_iterators
. Cómo eliminar la constness de un const_iterator
? Aunque los iteradores son una forma generalizada de punteros, pero const_iterator
e const_iterator
s son dos cosas diferentes. Por lo tanto, creo que tampoco puedo usar const_cast<>
para const_iterator
de const_iterator
a const_iterator
s.
Un enfoque podría ser que const_iterator
un iterador que se mueve hasta el elemento al que señala const_iterator
. Pero esto parece ser un algoritmo de tiempo lineal.
¿Alguna idea sobre cuál es la mejor manera de lograr esto?
En las respuestas a su publicación anterior, hubo un par de personas, yo incluido, que recomendaron usar const_iterators en su lugar por motivos no relacionados con el rendimiento. Legibilidad, trazabilidad desde la placa de diseño hasta el código ... Usar const_iterators para proporcionar acceso mutante a un elemento no const es mucho peor que nunca usar const_iterators en absoluto. Está convirtiendo su código en algo que solo usted comprenderá, con un diseño peor y un dolor real de mantenimiento. Usar const para descartarlo es mucho peor que no usar const.
Si está seguro de que lo quiere, la parte buena / mala de C ++ es que siempre puede obtener suficiente cuerda para ahorcarse. Si tu intención es usar const_iterator por problemas de rendimiento, realmente deberías reconsiderarlo, pero si aún quieres despegarte ... bueno, C ++ puede proporcionarte el arma que elijas.
Primero, el más simple: si sus operaciones toman los argumentos como const (incluso si aplica internamente const_cast), creo que debería funcionar directamente en la mayoría de las implementaciones (incluso si es probable que el comportamiento no esté definido).
Si no puede cambiar los funtores, puede abordar el problema desde cualquiera de los lados: proporcione un contenedor iterador no consistente alrededor de los iteradores de la constelación, o bien proporcione un contenedor functor const alrededor de los funtores no const.
Fachada de Iterator, el largo camino:
template <typename T>
struct remove_const
{
typedef T type;
};
template <typename T>
struct remove_const<const T>
{
typedef T type;
};
template <typename T>
class unconst_iterator_type
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef typename remove_const<
typename std::iterator_traits<T>::value_type
>::type value_type;
typedef value_type* pointer;
typedef value_type& reference;
unconst_iterator_type( T it )
: it_( it ) {} // allow implicit conversions
unconst_iterator_type& operator++() {
++it_;
return *this;
}
value_type& operator*() {
return const_cast<value_type&>( *it_ );
}
pointer operator->() {
return const_cast<pointer>( &(*it_) );
}
friend bool operator==( unconst_iterator_type<T> const & lhs,
unconst_iterator_type<T> const & rhs )
{
return lhs.it_ == rhs.it_;
}
friend bool operator!=( unconst_iterator_type<T> const & lhs,
unconst_iterator_type<T> const & rhs )
{
return !( lhs == rhs );
}
private:
T it_; // internal (const) iterator
};
Esta puede no ser la respuesta que usted quería, pero algo relacionada.
Supongo que quieres cambiar la cosa a la que apunta el iterador. La forma más simple que hago es que const_cast la referencia devuelta en su lugar.
Algo como esto
const_cast<T&>(*it);
Existe una solución con complejidad de tiempo constante en C ++ 11: para cualquier contenedor asociativo de secuencia, asociativo o desordenado (incluidos todos los contenedores de la Biblioteca estándar), puede llamar a la función miembro de borrado de rango con un rango vacío:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
Las funciones miembro de borrado de rango tienen un par de parámetros const_iterator
, pero devuelven un iterator
. Como se proporciona un rango vacío, la llamada a borrar no cambia el contenido del contenedor.
Sombrero propina a Howard Hinnant y Jon Kalb por este truco.
Lamentablemente, el tiempo lineal es la única forma de hacerlo:
iter i(d.begin());
advance (i,distance<ConstIter>(i,ci));
donde iter y constIter son typedefs adecuados y d es el contenedor sobre el que está iterando.
Pensé que sería divertido encontrar una solución para esto que funcione para contenedores que no están en la biblioteca estándar y no incluyan el método erase ().
Intentar utilizar esto hace que Visual Studio 2013 se cuelgue de la compilación. No incluyo el caso de prueba porque dejarlo a los lectores que pueden descubrir rápidamente la interfaz parece una buena idea; No sé por qué esto se cuelga en compilar. Esto ocurre incluso cuando const_iterator es igual a begin ().
// deconst.h
#ifndef _miscTools_deconst
#define _miscTools_deconst
#ifdef _WIN32
#include <Windows.h>
#endif
namespace miscTools
{
template < typename T >
struct deconst
{
static inline typename T::iterator iterator ( typename T::const_iterator*&& target, T*&& subject )
{
typename T::iterator && resultant = subject->begin ( );
bool goodItty = process < 0, T >::step ( std::move ( target ), std::move ( &resultant ), std::move ( subject ) );
#ifdef _WIN32
// This is just my habit with test code, and would normally be replaced by an assert
if ( goodItty == false )
{
OutputDebugString ( " ERROR: deconst::iterator call. Target iterator is not within the bounds of the subject container./n" )
}
#endif
return std::move ( resultant );
}
private:
template < std::size_t i, typename T >
struct process
{
static inline bool step ( typename T::const_iterator*&& target, typename T::iterator*&& variant, T*&& subject )
{
if ( ( static_cast <typename T::const_iterator> ( subject->begin () + i ) ) == *target )
{
( *variant ) += i;
return true;
}
else
{
if ( ( *variant + i ) < subject->end () )
{
process < ( i + 1 ), T >::step ( std::move ( target ), std::move ( variant ), std::move ( subject ) );
}
else { return false; }
}
}
};
};
}
#endif
Puede restar el iterador begin () del const_iterator para obtener la posición a la que apunta el const_iterator y luego agregar begin () de nuevo a ese para obtener un iterador sin const. No creo que esto sea muy eficiente para los contenedores no lineales, pero para los lineales, como el vector, esto llevará un tiempo constante.
vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
vector<int>::const_iterator ci = v.begin() + 2;
cout << *ci << endl;
vector<int>::iterator it = v.begin() + (ci - v.begin());
cout << *it << endl;
*it = 20;
cout << *ci << endl;
EDITAR : Esto parece funcionar solo para contenedores lineales (acceso aleatorio).
puede convertir su puntero de valor de iteración const a un puntero de valor no const y usarlo directamente algo como esto
vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(2);
vector<int>::const_iterator ci = v.begin() + 2;
cout << *ci << endl;
*const_cast<int*>(&(*ci)) = 7;
cout << *ci << endl;
aristeia.com/Papers/CUJ_June_2001.pdf sobre preferir iteradores sobre const_iterators responde a esto. La respuesta de Visage es la única alternativa segura previa a C ++ 11, pero en realidad es tiempo constante para iteradores de acceso aleatorio bien implementados y tiempo lineal para otros.