libreria iteradores ejemplos c++ constructor iterator explicit

iteradores - list iterator c++



C++ constructores e iteradores explĂ­citos (6)

Creo que dependerá de cómo std::vector<A> As(Iterator,Iterator) se implemente en su implementación particular del STL.

Considera el siguiente código:

#include <vector> struct A { explicit A(int i_) : i(i_) {} int i; }; int main() { std::vector<int> ints; std::vector<A> As(ints.begin(), ints.end()); }

¿Debería compilarse arriba? Mi sensación es que no debería, debido a que el constructor está marcado explicit .

Microsoft Visual C ++ acepta, dando un mensaje de error claro: cannot convert from ''int'' to ''const A''; Constructor for struct ''A'' is declared ''explicit'' cannot convert from ''int'' to ''const A''; Constructor for struct ''A'' is declared ''explicit''

Sin embargo, utilizando el compilador en línea de Comeau , el código se compila con éxito.

¿Cual es correcta?

Editar:

Curiosamente, el cambio de vector a set (después de agregar un operator < a A) hace que ambos compiladores den un error.

Sin embargo, cambiar el vector<int> para map<int, int> y vector<A> para map<A, A> hace que ambos compiladores acepten el código.


Esta es una pregunta bastante complicada, y podría ser que VisualStudio tenga razón y Comeau esté equivocado (esto parece muy difícil de creer).

El estándar si se lee palabra por palabra, define ese constructor de vector en términos del constructor de copia (ver cita), y eso significa literalmente que el objeto obtenido al desreferenciar el iterador primero debe convertirse al tipo T y luego el constructor de copia debe ser llamado. En este punto, con un constructor explícito, el código no debería compilarse.

Parece razonable esperar una implementación, por otro lado, para llamar directamente a un constructor tomando como argumento el iterador desreferenciado, en cuyo caso la llamada del constructor sería explícita y, por lo tanto, el código debería compilarse. Esto iría en contra de la redacción exacta en la siguiente cita, ya que el constructor de copia se define para un tipo dado T como un constructor que toma una única referencia posiblemente constante a un objeto de tipo T.

No puedo pensar en ningún argumento razonable para no utilizar el enfoque de Comeau, y mi opinión (esta es solo una opinión personal) es que la redacción en el estándar con respecto a la complejidad del constructor vectorial probablemente debería ser replanteada ya que requiere solo N llamadas a el constructor T apropiado , cuando corresponda, debería definirse como el constructor que coincide con la llamada T( *first ) (es decir, un constructor que toma un InputIterator::value_type (por valor o posiblemente referencia constante), o la copia T constructor después de una conversión implícita de InputIterator::value_type a T.

23.2.4.1 [lib.vector.cons] / 1

Complejidad: el vector de plantilla constructor (InputIterator primero, InputIterator last) hace solo N llamadas al constructor de copia de T (donde N es la distancia entre primero y último) y no reasignaciones si los iteradores primero y último son de avance, bidireccional o aleatorio categorías de acceso. Realiza N órdenes al constructor de copias de T y ordena las reasignaciones de N si solo son iteradores de entrada.

Me gustaría saber cómo se comporta el compilador VS cuando se le da:

struct T1; struct T2 { operator T1 (); }; struct T1 { T1( T2 const & ) { std::cout << "T1(T2)" << std::endl; } }; T2::operator T1() { std::cout << "T2::operator T1" << std::endl; return T1(*this); } int main() { std::vector<T2> v2; v2.push_back( T2() ); std::vector<T1> v1( v2.begin(), v2.end() ); }

Con g ++, el resultado es que no se llama al T2::operator T1 , sino que los elementos en v1 se construyen directamente a partir de los elementos en v2 . Supongo que con VS el compilador usaría T2::operator T1 para convertir de cada elemento en v2 a un elemento T1 y luego llamar al constructor de copia. ¿Es eso así?


Este código no se compila en Comeau:

class Foo { public: explicit Foo(int bar) { } }; class Bar { void DoStuff(Foo foo){ } void DoStuff2() { DoStuff(4); } };

Mensaje de error:

"ComeauTest.c", line 16: error: no suitable constructor exists to convert from "int" to "Foo" DoStuff(4); ^ 1 error detected in the compilation of "ComeauTest.c".

Entonces, en el nivel rudimentario, el compilador en línea admite constructores explícitos. Debe ser algo relacionado con el vector / iteradores.

EDITAR Sin embargo, compila:

Foo foo = (Foo)5;

Que es una conversión explícita, así que está bien. Supongo que la clase vectorial de Comeau hace un molde explícito en el constructor en alguna parte, donde la biblioteca de Microsoft no lo hace.

Más sobre constructores explícitos - http://www.glenmccl.com/tip_023.htm


Esto realmente se reduce a una cuestión de cómo se implementa la biblioteca STL, no un problema de especificación del lenguaje. No hay nada en la especificación del lenguaje que prohíba que funcione, ni hay nada que requiera que funcione.

Si el constructor stl :: vector se escribiera para probar una conversión implícita utilizando el operador de asignación, fallaría. Es más probable que la implementación de Microsoft STL haga uso de la optimización del valor de retorno durante la inicialización a través de una llamada de constructor, en cuyo caso este código funcionaría correctamente.

Es importante tener en cuenta que la única razón por la que esto funciona es porque el constructor stl :: vector está modelado, y el único requisito es que sea un input_iterator, o más exactamente que admita toda la funcionalidad requerida de un iterador de entrada.

También me gustaría señalar que este es un excelente ejemplo de por qué a menudo es difícil escribir código multiplataforma. A veces terminas con problemas donde ninguno de los compiladores necesariamente se desvía del estándar de lenguaje, pero el código aún no es portátil.


Miré a través de la implementación de STL de GCC y debería tener un comportamiento similar. Este es el por qué.

  • Los elementos de un vector se inicializan mediante una plantilla de función genérica que acepta dos tipos cualquiera X y V y llama a new( p ) X( v ) donde v es una V (estoy parafraseando un bit). Esto permite una conversión explícita.
  • Los elementos de un set o map son inicializados por una función miembro privada de _tree<T,…> que espera específicamente que se transfiera una T const & . Esta función miembro no es una plantilla (más allá de ser un miembro de una plantilla), entonces, si el valor inicial no puede convertirse implícitamente a T , la llamada falla. (De nuevo estoy simplificando el código).

El estándar no requiere que la conversión explícita funcione o que la conversión implícita no funcione cuando se inicializa un contenedor con un rango. Simplemente dice que el rango se copia en el contenedor. Definitivamente ambiguo para su propósito.

Sorprende la ambigüedad que existe, considerando cómo ya han refinado el estándar en consideración a problemas como el que tuve hace un par de semanas.


Sí, debería compilar. Si el constructor no se usa, su explicitud no es un problema.