2009-05-28 10 views
5

¿Cómo se modifica el siguiente código para permitir que la función de plantilla ask_runUI() use s_EOF sin hacer s_EOF público?¿Cómo permitir que la función de plantilla tenga acceso de amigo (como)?

#include <string> 
#include <iostream> 
#include <sstream> 
#include <vector> 
class AskBase { 
protected: 
    std::string m_prompt; 
    std::string m_answer; 
    virtual bool validate(std::string a_response) = 0; 
public: 
    AskBase(std::string a_prompt):m_prompt(a_prompt){} 
    std::string prompt(){return m_prompt;} 
    std::string answer(){return m_answer;} 
    static int const s_EOF = -99; 
    static int const s_BACKUP = -1; 
    static int const s_OK = 1; 
    int ask_user(); 
}; 
template<typename T> class Ask : public AskBase{ 
public: 
    Ask(std::string a_prompt):AskBase(a_prompt){} 
    bool validate(std::string a_response); 
}; 
template<> bool Ask<std::string>::validate(std::string a_response){return true;} 
template<> bool Ask<int>::validate(std::string a_response){int intAnswer; 
    return (std::stringstream(a_response) >> intAnswer);} 
int AskBase::ask_user(){ 
    for(;;){ 
     std::cout << "Enter " << m_prompt; 
     std::string response; 
     getline(std::cin, response); 
     if (std::cin.eof()) 
      return s_EOF; 
     else if (response == "^") 
      return s_BACKUP; 
     else if (validate(response)){ 
      m_answer = response; 
      return s_OK; 
     } 
    } 
    return s_EOF; 
} 
template<typename T> int ask_runUI(T& a_ui){ 
    int status = AskBase::s_OK; 
    for (typename T::iterator ii=a_ui.begin(); 
      status!=AskBase::s_EOF && ii!=a_ui.end(); 
      ii+=((status==AskBase::s_BACKUP)?((ii==a_ui.begin())?0:-1):1) 
     status = (*ii)->ask_user(); 
    return (status == AskBase::s_OK); 
} 
int main(){ 
    std::vector<AskBase*> ui; 
    ui.push_back(new Ask<std::string>("your name: ")); 
    ui.push_back(new Ask<int>("your age: ")); 
    if (ask_runUI(ui)) 
     for (std::vector<AskBase*>::iterator ii=ui.begin(); ii!=ui.end(); ++ii) 
      std::cout << (*ii)->prompt() << (*ii)->answer() << std::endl; 
    else 
     std::cout << "\nEOF\n"; 
} 
+0

La próxima vez, utilice 'int ask_runUI()' (el '' marcar como código). –

+0

Si devuelve el número, ¿por qué debería ser privado? –

+0

Expandí el ejemplo para mostrar que solo AskBase :: ask_user() devuelve s_EOF que solo utiliza ask_runUI(). –

Respuesta

22

Si desea que una función de plantilla sea un amigo, debe decirlo en la declaración de la clase. Cambiar la línea que dice que la función de amigo a esto:

template <typename T> 
friend int ask_runUI(T& a_ui); 

Ahora, si la clase es en sí misma una plantilla, las cosas se ponen mucho más complicado. Los amigos de plantilla no son triviales para hacer correctamente. Para eso, lo referiré a lo que dice C++ FAQ Lite sobre el tema.

-2

La más sencilla es probablemente para reemplazar static int const miembros con la enumeración y no meterse con friend s:


class AskBase { 
public: 
    enum { Eof = -99, Null = 0, Ok = 1, Backup = -1 }; 
    ... 
}; 
+0

-1. No veo cómo esto cambia nada: su enumeración, que ha declarado pública, es en todos los aspectos equivalentes a simplemente hacer públicos los miembros estáticos de const int. Si hay alguna distinción que me he perdido, por favor dígalo y lo reconsideraré. –

+0

La distinción es que el compilador no tiene que asignar espacio para variables de miembros estáticos. La ventaja es que es SIMPLE. –

+0

Bueno, investigué y es más raro de lo que pensaba. Por ejemplo, si declara e inicializa los miembros de const int estáticos dentro de la definición de clase * sin * definirlos en el nivel de espacio de nombres (es decir, sin escribir "statict const int AskBase :: s_Eof;" etc. a nivel de espacio de nombres) entonces no habrá espacio alguna vez ser asignado. PERO, el compilador solo le permitirá referirse a s_Eof como un valor r (por ejemplo, el enlace a una referencia no constante fallará en el momento del enlace). Consulte aquí: http://stackoverflow.com/questions/272900/c-undefined -reference-to-static-class-member, especialmente la respuesta de Richard Corden. –

2

Esto funcionó para mí!

class AskBase { 
public: 
    AskBase(){} 
    template<typename T> 
    friend int ask_runUI(T& a_ui); 
private: 
    static int const s_EOF = -99; 
    static int const s_BACKUP = -1; 
    static int const s_NULL = 0; 
    static int const s_OK = 1; 
}; 
//int ask_runUI() 
template<typename T> 
int ask_runUI(T& a_ui) 
{ 
    return AskBase::s_NULL; 
} 
+1

Esa es la clásica antipatrón para crear un modelo de plantilla. Básicamente es lo mismo que hacer que todos los miembros sean públicos ya que cualquiera puede escribir su propia versión de ask_runUI() –

+3

. Pueden hacerlo, pero ¿lo harán por accidente, sin darse cuenta de que eso es lo que están haciendo? Especialmente si ask_UI está en el mismo espacio de nombres que la clase, si hurgan dentro de la interfaz de una clase, ¿seguramente sabrán que están rompiendo la encapsulación? –

Cuestiones relacionadas