2015-05-15 29 views
7

Chcę zaimplementować prostą strukturę danych drzewa wyrażeń arytmetycznych w języku C++, tak aby obiekt drzewa wyrażeń został zainicjowany przez: ExprTree(operator, expression1, expression2). Oto przykład jak to powinno działać:Struktura danych drzewa ekspresji

double x = 1, y = 2, z = 0.5; 
expr1 = ExprTree('*', x, y); // expr1 = 1 * 2 = 2 
expr2 = ExprTree('-', expr1, z); // expr2 = (1 * 2) - 0.5 = 1.5 
cout << expr2.str() << endl; // ((1 * 2) - 0.5) 
cout << expr2.eval() << endl; // 1.5 

Oto jak mój kod wygląda tak daleko:

template<class operand_type> 
class ExprTree 
{ 
public: 
    ExprTree(const char op_, operand_type& operand1_, operand_type& operand2_) 
    { 
     op = op_; 
     operand1 = operand1_; 
     operand2 = operand2_; 
    } 
    double eval() const; 
    std::string str() const; 
private: 
    char op; 
    typename operand_type operand1, operand2; 
}; 

template<class operand_type> 
std::string ExprTree<operand_type>::str() const 
{ 
    std::ostringstream os; 
    std::string op1, op2; 
    if (typeid(*operand1) == typeid(ExprTree)) 
     op1 = operand1->str(); 
    else 
     op1 = std::string(*operand1); 
    if (typeid(*operand2) == typeid(ExprTree)) 
     op2 = operand1->str(); 
    else 
     op2 = std::string(*operand2); 
    os << "(" << op1 << " " << op << " " << op2 << ")"; 
    return os.str(); 
} 

Jednak uzyskać ten błąd podczas kompilacji kodu:

left of '->write' must point to class/struct/union/generic type 

Byłbym wdzięczny, gdyby ktoś pomógł mi z tym błędem i ewentualnie podać kilka wskazówek, w jaki sposób powinienem wdrożyć tę strukturę danych. Btw, jestem bardzo nowy w C++.

+1

Nie widzę odpowiedniego fragmentu kodu (wezwanie do "zapisu" lub jego definicji). – Unimportant

+0

Edytowałem kod. Powinien przeczytać 'str' zamiast' write'. – Randolph

+0

Musisz dodać typ szablonu podczas tworzenia obiektu: 'ExprTree expr1 ('*', x, y);' Następny wiersz 'ExprTree ('-', expr1, z)' wymaga konstruktora, który może podjąć 2 różne typy operandów. – Unimportant

Odpowiedz

2

Istnieje szereg problemów w kodzie:

  1. użyć członek wskaźnik-> operator na zmiennych składowych operand1 i operand2

  2. Trzeba dwa różne typy w argumentach szablonu, aby zainicjować obiekt z różnymi typami argumentów.

  3. Klasy/konstruktorzy nie wykrywają automatycznie typów takich jak funkcje. Oznacza to, że nie można zrobić czegoś takiego jak ExprTree('*', x, y);. Masz ether określić argumenty szablonu lub użyć dodatkowej funkcji szablonu do skonstruowania obiektu klasy szablonu ExprTree. Zobacz ten answer.

  4. if (typeid(*operand1) == typeid(ExprTree)) ocenia w czasie wykonywania, więc dostaniesz błąd kompilacji, ponieważ starają się wywołać metodę str() i przekazać ten sam obiekt do std :: string

I woleliby następujące rozwiązanie:

#include <string> 
#include <iostream> 
#include <sstream> 

template<typename operand_type_A, typename operand_type_B> 
class ExprTree 
{ 
public: 
    ExprTree(){}; 
    ExprTree(const char op_, const operand_type_A& operand1_, const operand_type_B& operand2_) { 
     op = op_; 
     operand1 = operand1_; 
     operand2 = operand2_; 
    }; 
    double eval() const; 
    std::string str() const; 

private: 
    char op; 
    operand_type_A operand1; 
    operand_type_B operand2; 
}; 

template<typename operand_type_A, typename operand_type_B> 
ExprTree<operand_type_A, operand_type_B> makeExpr(const char op, const operand_type_A& operand1, const operand_type_B& operand2) 
{ 
    return ExprTree<operand_type_A, operand_type_B>(op, operand1, operand2); 
} 

template<typename T> 
std::string ToString(const T& x) 
{ 
    return x.str(); 
} 

template<> 
std::string ToString<double>(const double& x) 
{ 
    return std::to_string(x); 
} 

template<typename operand_type_A, typename operand_type_B> 
std::string ExprTree<operand_type_A, operand_type_B>::str() const { 
    std::ostringstream os; 
    std::string op1, op2; 
    op1 = ToString(operand1); 
    op2 = ToString(operand2); 
    os << "(" << op1 << " " << op << " " << op2 << ")"; 
    return os.str(); 
} 

int main() 
{ 
    double x = 1, y = 2, z = 0.5; 
    std::cout << makeExpr('-', makeExpr('*', x, y), z).str() << std::endl; 
    return 0; 
} 

wyprowadza następujący ciąg:

((1.000000 * 2.000000) - 0.500000) 

Możesz spróbować tego here.

2

Kiedy mówisz:

operand1->str(); 

Należy powiedzieć zamiast:

operand1.str(); 

Ponieważ operand1 nie jest wskaźnikiem, ale zmienna członkiem.

Komunikat o błędzie

lewo od '-> Str' musi wskazywać na klasy/struct/union/typ rodzajowy

zasadniczo mówi, że lewa wykonawcy -> musi być wskaźnikiem (nie jest). (Mówi też, że musi wskazywać na klasę lub coś podobnego, na przykład nie na liczbę całkowitą).