Acceso de clase de plantilla derivado a datos de miembro de clase base

6 minutos de lectura

Acceso de clase de plantilla derivado a datos de miembro de clase base
hámster

Esta pregunta es una continuación de la que se hace en este hilo.

Usando las siguientes definiciones de clase:

template <class T>
class Foo {

public:
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
    {
        /* do something for foo */
    }
    T Foo_T;        // either a TypeA or a TypeB - TBD
    foo_arg_t _foo_arg;
};

template <class T>
class Bar : public Foo<T> {
public:
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
    : Foo<T>(bar_arg)   // base-class initializer
    {

        Foo<T>::Foo_T = T(a_arg);
    }

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
    : Foo<T>(bar_arg)
    {
        Foo<T>::Foo_T = T(b_arg);
    }

    void BarFunc ();

};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl;   // This doesn't work - compiler error is: error: ‘_foo_arg’ was not declared in this scope
    std::cout << Bar<T>::_foo_arg << std::endl;   // This works!
}

Al acceder a los miembros de la clase base de la clase de plantilla, parece que siempre debo calificar explícitamente a los miembros usando la sintaxis de estilo de plantilla de Bar<T>::_foo_arg. Hay alguna manera de evitar esto? ¿Puede entrar en juego una declaración/directiva ‘usando’ en un método de clase de plantilla para simplificar el código?

Editar:

El problema del alcance se resuelve calificando la variable con esta sintaxis->.

Acceso de clase de plantilla derivado a datos de miembro de clase base
algo

Puedes usar this-> para dejar claro que te refieres a un miembro de la clase:

void Bar<T>::BarFunc () {
    std::cout << this->_foo_arg << std::endl;
}

Alternativamente, también puede usar “using” en el método:

void Bar<T>::BarFunc () {
    using Bar<T>::_foo_arg;             // Might not work in g++, IIRC
    std::cout << _foo_arg << std::endl;
}

Esto le deja claro al compilador que el nombre del miembro depende de los parámetros de la plantilla para que busque la definición de ese nombre en los lugares correctos. Para obtener más información, consulte también esta entrada en C++ Faq Lite.

  • El enlace a las preguntas frecuentes es muy útil: también muestra dónde este problema puede causar un comportamiento no deseado de forma invisible.

    – xtofl

    13 de julio de 2009 a las 19:00

  • Alguna idea por qué ¿Esto es verdad? (las preguntas frecuentes no responden esto completamente)

    – Catskul

    1 de octubre de 2010 a las 1:26

Acceso de clase de plantilla derivado a datos de miembro de clase base
cancionyuanyao

Aquí, la clase base no es una clase base no dependiente (lo que significa una con un tipo completo que se puede determinar sin conocer los argumentos de la plantilla), y _foo_arg es un nombre no dependiente. C++ estándar dice que los nombres no dependientes no se buscan en las clases base dependientes.

Para corregir el código, basta con hacer el nombre _foo_arg dependiente porque los nombres dependientes se pueden buscar solo en el momento de la creación de instancias, y en ese momento se sabrá la especialización base exacta que se debe explorar. Por ejemplo:

// solution#1
std::cout << this->_foo_arg << std::endl;

Una alternativa consiste en introducir una dependencia usando un nombre calificado:

// solution#2
std::cout << Foo<T>::_foo_arg << std::endl;

Se debe tener cuidado con esta solución, porque si el nombre no dependiente no calificado se usa para formar una llamada de función virtual, entonces la calificación inhibe el mecanismo de llamada virtual y el significado del programa cambia.

Y puede traer un nombre de una clase base dependiente en la clase derivada una vez por using:

// solution#3
template <class T>
class Bar : public Foo<T> {
public:
    ...
    void BarFunc ();
private:
    using Foo<T>::_foo_arg;
};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl;   // works
}

Parece funcionar bien en Visual C ++ 2008. He agregado algunas definiciones ficticias para los tipos que mencionó pero no proporcionó ninguna fuente. El resto es exactamente como lo pones. Entonces una función principal para forzar BarFunc para ser instanciado y llamado.

#include <iostream>

class streamable {};
std::ostream &operator<<(std::ostream &os, streamable &s) { return os; }

class foo_arg_t : public streamable {};
class a_arg_t : public streamable {};
class b_arg_t : public streamable  {};

template <class T>
class Foo {

public:
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
    {
        /* do something for foo */
    }
    T Foo_T;        // either a TypeA or a TypeB - TBD
    foo_arg_t _foo_arg;
};

template <class T>
class Bar : public Foo<T> {
public:
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
    : Foo<T>(bar_arg)   // base-class initializer
    {

        Foo<T>::Foo_T = T(a_arg);
    }

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
    : Foo<T>(bar_arg)
    {
        Foo<T>::Foo_T = T(b_arg);
    }

    void BarFunc ();

};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl; 
    std::cout << Bar<T>::_foo_arg << std::endl;   
}

int main()
{
    Bar<a_arg_t> *b = new Bar<a_arg_t>(foo_arg_t(), a_arg_t());
    b->BarFunc();
}

  • g ++ entrega muchos errores con respecto a las definiciones en la parte superior. Sin embargo, el problema del alcance aún persiste con: “error: ‘_foo_arg’ no se declaró en este alcance”, debido a la primera llamada a _foo_arg en la definición de BarFunc().

    – hámster

    13 de julio de 2009 a las 17:41

  • ¿Quiere decir que mis declaraciones de tipo ficticio le dan errores en gcc?

    – Daniel Earwicker

    13 de julio de 2009 a las 17:51

  • sí, los tipos ficticios en la parte superior, pero el error de alcance también permanece.

    – hámster

    13 de julio de 2009 a las 17:52

  • Creo que g ++ puede ser correcto re: su problema original. El compilador de IBM armó el mismo alboroto, IIRC.

    – Daniel Earwicker

    13 de julio de 2009 a las 17:53

  • Lo siento, agregué un código de prueba, eso me estaba dando el error. El código que publicaste se compila si usas this->_foo_arg en lugar de _foo_arg en BarFunc().

    – hámster

    13 de julio de 2009 a las 18:02

¿Ha sido útil esta solución?