Come permettere a un modello di funzione di avere un amico (come) l’accesso?

Come si fa a modificare il codice riportato di seguito per consentire la funzione di modello ask_runUI() utilizzare s_EOF senza fare s_EOF pubblico?

#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";
}
  • La prossima volta, si prega di utilizzare int ask_runUI() (il ` marchio come codice).
  • Se si restituisce il numero, perché dovrebbe essere privato?
  • Ho ampliato l’esempio per dimostrare che solo AskBase::ask_user() restituisce s_EOF che è utilizzato solo da ask_runUI().
InformationsquelleAutor CW Holeman II | 2009-05-28



3 Replies
  1. 28

    Se si desidera un modello di funzione di essere un amico, è necessario dire nella dichiarazione di classe. Modificare la riga che dichiara la funzione di amico per questo:

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

    Ora, se la tua classe è di per sé un modello, le cose si fanno molto più complicate. Modello amici non sono banali per fare correttamente. Per questo, mi rifaccio a ciò che C++ FAQ Lite dice sull’argomento.

  2. 2

    Questo ha funzionato per me!

    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;
    }
    • Questo è il classico anti-pattern per il dare amicizia a un modello di metodo. È fondamentalmente lo stesso come fare a tutti i membri del pubblico, come chiunque può scrivere la propria versione di ask_runUI()
    • Si può, ma saranno loro a fare così per caso, senza sapere di che cosa stanno facendo? Soprattutto se ask_UI è nello stesso spazio dei nomi della classe, se vanno a frugare all’interno di una classe di interfaccia, sicuramente sanno che stai rompendo incapsulamento?
  3. -2

    Il più semplice è probabilmente quello di sostituire static int const membri con l’enumerazione e non pasticciare con friends:

    
    class AskBase {
    public:
        enum { Eof = -99, Null = 0, Ok = 1, Backup = -1 };
        ...
    };
    • -1. Non vedo come questo cambi qualcosa — il enum, che è stata dichiarata di pubblico, è in ogni modo materiale equivalente per il static const int membri del pubblico. Se c’è qualche distinzione che ho perso, si prega di dire e io riconsiderare.
    • La distinzione è che il compilatore non deve allocare lo spazio per le variabili membro statico. Il vantaggio è che è SEMPLICE.
    • Bene, ho fatto qualche ricerca ed è più strane che ho pensato. Per uno, se è sufficiente dichiarare e inizializzare l’static const int membri all’interno della definizione della classe senza definizione, a livello dello spazio dei nomi (cioè senza scrivere “statict const int AskBase::s_Eof;” etc. a livello dello spazio dei nomi) quindi nessuno spazio sarà mai assegnato. MA, il compilatore permette solo di vedere s_Eof come un rvalue (ad esempio, l’associazione a un non-const rif riuscirà a link!!) Vedi qui: stackoverflow.com/questions/272900/…, soprattutto Richard Corden risposta.

Lascia un commento