Tengo una plantilla de clase Foo con la siguiente función miembro:

bool contains(const T& item) const

He creado una instancia de esto con un tipo de puntero: Foo<Bar*>, lo que me lleva a esperar que la función miembro ahora tenga la siguiente firma:

bool contains(const Bar*& item) const

En una función miembro de const Bar, intento pasar this a Foo<Bar*>::contains:

bool Bar::func(const Foo<Bar*>& foo) const
{
    return foo.contains(this);
}

Esto no se puede compilar, con el siguiente error:

error: conversión no válida de "const Bar*" a "Bar*"

Pregunta:

  • ¿Por qué mi parámetro const T& no es constante?
  • ¿Qué firma para Foo<T>::contains(...) const se requiere para permitir la compilación de llamadas con this?

Ejemplo completo:

#include <vector>
#include <algorithm>

template<typename T>
struct Foo
{
    bool contains(const T& item) const
    {
        return false;
    }
};

struct Bar
{
    bool func(const Foo<Bar*>& foo) const
    {
        return foo.contains(this);
    }
};

Salida de error:

scratch/main.cpp:17:33: error: invalid conversion from ‘const Bar*’ to ‘Bar*’ [-fpermissive]
         return foo.contains(this);
                                 ^
scratch/main.cpp:7:10: note: initializing argument 1 of ‘bool Foo<T>::contains(const T&) const [with T = Bar*]’
     bool contains(const T& item) const
3
Steve Lorimer 25 ene. 2016 a las 22:30

2 respuestas

La mejor respuesta

He creado una instancia de esto con un tipo de puntero: Foo<Bar*>, que me lleva esperar que la función miembro ahora tenga lo siguiente firma:

bool contains(const Bar*& item) const

Ahí es donde está el problema. Cuando T = Bar*, la expresión

bool contains(const T& item) const

Realmente se compilará para

bool contains(Bar * const & item) const

Es decir, una referencia a un puntero a barra constante. Tiene sentido que lo pienses: quieres que T sea constante, y luego quieres una referencia a eso.

Si desea aplicar la constante de la forma habitual "prevista" (aunque esto podría causar algunas sorpresas para los programadores experimentados de C ++), puede declarar su función de contenedor y miembro de la siguiente manera:

template <class T>
class Container {
public:
    using const_bare_type = typename std::conditional<
        std::is_pointer<T>::value,
        typename std::remove_pointer<T>::type const*,
        const T>::type;

    bool contains(const const_bare_type& item);
};
6
Yam Marcovic 25 ene. 2016 a las 20:02

Alertas del compilador sobre una línea incorrecta, debe escribir:

bool func(const Foo<const Bar*>& foo) const

Es decir. const Bar* en el parámetro de plantilla, porque Bar::func recibe const Bar * this como su parámetro y no puede convertirlo a Bar* en el parámetro de plantilla (no se puede eliminar const).

0
vladon 25 ene. 2016 a las 19:34