2009-05-28 14 views
5

Jak zmodyfikować poniższy kod, aby umożliwić funkcję szablonu ask_runUI() używać s_EOF bez publicznego udostępniania s_EOF?Jak zezwolić na funkcję szablonu, aby mieć dostęp do znajomych (jak)?

#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

Następnym razem użyj 'int ask_runUI()' ("oznacz to jako kod). –

+0

Jeśli zwracasz numer, dlaczego ma być prywatny? –

+0

Poszerzyłem przykład, aby pokazać, że tylko AskBase :: ask_user() zwraca s_EOF, który jest używany tylko przez ask_runUI(). –

Odpowiedz

22

Jeśli chcesz, aby funkcja szablonu była przyjaźnią, musisz to powiedzieć w deklaracji klasy. Zmień linię, która deklaruje funkcję znajomego:

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

Teraz, jeśli twoja klasa sama jest szablonem, sprawy stają się o wiele bardziej skomplikowane. Znajomi z szablonu nie są banalni, aby robić to poprawnie. W tym celu skieruję Cię do tego, co C++ FAQ Lite mówi na ten temat.

-2

Najprostszy jest prawdopodobnie zastąpić static int const członków z wyliczeniem i nie bałagan z friend s:


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

-1. Nie widzę, jak to zmienia cokolwiek - twoje wyliczenie, które zadeklarowałeś publicznie, jest w każdym materialnym znaczeniu równoważne z upublicznieniem statycznych członków. Jeśli jest jakaś różnica, którą przeoczyłem, proszę powiedz, a ja się zastanowię. –

+0

Rozróżnienie polega na tym, że kompilator nie musi przydzielać miejsca dla statycznych zmiennych składowych. Zaletą jest to, że jest PROSTA. –

+0

Cóż, zrobiłem trochę badań i jest to dziwniejsze niż myślałem. Po pierwsze, jeśli po prostu zadeklarujesz i zainicjujesz statyczne elementy const int wewnątrz definicji klasy * bez * zdefiniowania ich na poziomie przestrzeni nazw (tj. Bez zapisu "statict const int AskBase :: s_Eof;" itd. Na poziomie przestrzeni nazw), wtedy nie będzie miejsca kiedykolwiek zostaną przydzielone. ALE, kompilator zezwoli ci tylko na odwołanie się do s_Eof jako rwartości (np. Powiązanie z nie-const ref nie powiedzie się w czasie łącza !!) Zobacz tutaj: http://stackoverflow.com/questions/272900/c-undefined -referencja-do-statystyki-klasy-członka, szczególnie Richarda Cordena. –

2

Ten pracował dla mnie!

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

To klasyczny anty-wzór do dostosowania metody szablonowej. Zasadniczo jest to takie samo, jak upublicznienie wszystkich członków, ponieważ każdy może teraz napisać własną wersję ask_runUI() –

+3

Mogą, ale czy zrobią to przez przypadek, nie zdając sobie sprawy, że to właśnie robią? Zwłaszcza jeśli ask_UI znajduje się w tej samej przestrzeni nazw co klasa, jeśli zaczną grzebać w interfejsie klasy, to na pewno wiedzą, że zrywają hermetyzację? –