c++ - Tipos de retorno, decltype y constness continuos
c++11 const-correctness (1)
Simplemente estaba experimentando con los nuevos tipos de retorno final, donde encontré un problema con este código (simplificado)
#include <list>
class MyContainer{
std::list<int> ints;
auto begin( ) -> decltype(ints.begin())
{
return ints.begin();
}
auto begin( ) const -> decltype(ints.begin())
{
return ints.begin();
}
};
Ignora el hecho de cuán inútil es este código. La parte importante es el error del compilador generado cuando se usa GCC 4.6.1 (con el -std=c++0x
):
In member function ''std::list<int>::iterator MyContainer::begin() const'':
error: could not convert ''((const MyContainer*)this)->MyContainer::ints.std::list<_Tp, _Alloc>::begin [with _Tp = int, _Alloc = std::allocator<int>, std::list<_Tp, _Alloc>::const_iterator = std::_List_const_iterator<int>]()'' from ''std::list<int>::const_iterator {aka std::_List_const_iterator<int>}'' to ''std::list<int>::iterator {aka std::_List_iterator<int>}''
En caso de que no sea un fanático de los errores relacionados con las plantillas, la historia corta es que en el cuerpo de la versión const
de MyContainer::begin
, la expresión ints.begin()
devuelve un valor de tipo std::list<int>::const_iterator
(ya que std::list<int>::const_iterator
es const
en dicho contexto). Sin embargo, decltype(ints.begin())
produce el tipo std::list<int>::iterator
decltype
, es decir, decltype
ignora el calificador const
del método begin
cuando decide el tipo de expresión. Como era de esperar, un conflicto en los tipos es el resultado.
Esto me parece ser un error en el compilador GCC. Solo tendría sentido para decltype
honrar el calificador const
y producir el tipo const_iterator
. ¿Alguien puede confirmar o negar (tal vez incluso explicar) esto? Tal vez estoy pasando por alto algo en la mecánica de decltype
, pero esto parece un escenario bastante sencillo.
Nota: por lo que puedo decir, el mismo comportamiento se mantiene no solo para std::list<int>
, sino para cualquier tipo con funciones miembro sobrecargadas en constness que devuelven tipos incompatibles.
Tienes razón, esto es un error. Según N3291, sección 5.1.1, párrafo 3:
Si una declaración declara una función miembro o una plantilla de función miembro de una clase X, la expresión es un valor predefinido de tipo "puntero a cv-quali fi er-seq X" entre el cv-qualifer-seq opcional y el final de la función de definición , miembro-declarador, o declarador. No aparecerá antes del cv-quali fi er-seq opcional y no aparecerá dentro de la declaración de una función miembro estática (aunque su tipo y categoría de valor se definen dentro de una función miembro estática, ya que están dentro de una función miembro no estática) . [Nota: esto se debe a que la coincidencia de la declaración no se produce hasta que se conozca el declarador completo. —Endre nota] A diferencia de la expresión de objeto en otros contextos, * no se requiere que sea de tipo completo para fines de acceso de miembro de clase (5.2.5) fuera del cuerpo de la función miembro. [Nota: solo los miembros de la clase declarados antes de la declaración son visibles. "Nota final"
Pero esto fue un cambio reciente entre el último borrador de trabajo y N3291. Así que GCC tenía razón hace menos de 6 meses; ese es el peligro de escribir código a una especificación en movimiento.