Invalid argument type to unary expression

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Formula {
public:
    virtual bool isTrue() const = 0;
};

class VariableFormula : Formula {
protected:
    bool value;
public:
    VariableFormula(bool val) : value(val) {}
    virtual bool isTrue() const {
        return value;
    }
    VariableFormula operator!() {
        return VariableFormula(!value);
    }
};

class OrFormula;

class AndFormula : Formula {
protected:
    Formula& firstFormula;
    Formula& secondFormula;
public:
    AndFormula(Formula& first, Formula& second) : firstFormula(first), secondFormula(second) {}
    virtual bool isTrue() const {
        if(firstFormula.isTrue() && secondFormula.isTrue())
            return true;
        return false;
    }
    OrFormula operator!();
};

class OrFormula : AndFormula {
public:
    virtual bool isTrue() const {
        if(firstFormula.isTrue() || secondFormula.isTrue())
            return true;
        return false;
    }
    AndFormula operator!() {
        return AndFormula(!firstFormula, !secondFormula);
    }
};

OrFormula AndFormula::operator!() {
    return OrFormula(!firstFormula, !secondFormula);
}


The problem is that the compiler reacts with the error message "Invalid argument type 'Formula' to unary expression" for the return statements in AndFormula's and OrFormula's operator! functions. It doesn't seem logical to me since the base class is abstract and thus cannot be instantiated, so each Formula is either an AndFormula or an OrFormula, and the recursion means that negating a Formula should always eventually land in the base case, which is the negation of a VariableFormula. Why is this happening? How can I fix it?
Last edited on
Since firstFormula and secondFormula are references to a Formula (Formula&) you can only use the interface provided by the Formula class.
You're right. But it causes problems for me with this setup. I cannot define a Formula-type !operator function in Formula because I cannot return a Formula (cannot be instantiated because it's an abstract type). The fact that each subclass has a different return type for the (same) operator! function means that in C++ they're not true subclasses to Formula. So taking away the abstractness of the base class is not enough.
Last edited on
Something along these lines, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <memory>

struct abstract_formula {

    virtual ~abstract_formula() = default ;
    virtual bool is_true() const = 0;

    using pointer = std::shared_ptr<abstract_formula> ;
};

struct variable_formula : abstract_formula {

    bool value = false ;

    explicit variable_formula( bool b ) : value(b) {}
    virtual bool is_true() const override { return value ; }
};

struct and_formula : abstract_formula {

    pointer first ;
    pointer second ;

    explicit and_formula( pointer a, pointer b ) : first(a), second(b) {}

    virtual bool is_true() const override {

        return first && first->is_true() && second && second->is_true() ; }
};

struct or_formula : abstract_formula {

    pointer first ;
    pointer second ;

    explicit or_formula( pointer a, pointer b ) : first(a), second(b) {}

    virtual bool is_true() const override {

        return ( first && first->is_true() ) || ( second && second->is_true() ) ; }
};

struct not_formula : abstract_formula {

    pointer formula ;

    explicit not_formula( pointer a_formula ) : formula(a_formula) {}

    virtual bool is_true() const override {

        return !( formula && formula->is_true() ) ;
    }
};

// facade which collapses the formula hierarchy into a simple type which uses value semantics
struct Formula {

    using pointer = abstract_formula::pointer ;

    bool is_true() const { return impl && impl->is_true() ; }

    Formula( bool b ) : impl( std::make_shared<variable_formula>(b) ) {}  
    Formula( pointer p ) : impl(p) {}

    friend Formula operator&& ( Formula a, Formula b ) {

        return Formula{ std::make_shared<and_formula>( a.impl, b.impl ) } ;
    }

    friend Formula operator|| ( Formula a, Formula b ) {

        return Formula{ std::make_shared<or_formula>( a.impl, b.impl ) } ;
    }

    friend Formula operator! ( Formula f ) {

        return Formula{ std::make_shared<not_formula>( f.impl ) } ;
    }

    pointer impl ;
};

int main()
{
    const Formula a_formula = ( Formula(true) || false ) && true && !Formula(false) ;

    std::cout << std::boolalpha << a_formula.is_true() << '\n' // true
              << (!a_formula).is_true() << '\n' // false
              << !a_formula.is_true() << '\n' // false
              << ( a_formula && true ).is_true() << '\n' // true
              << ( a_formula || !a_formula ).is_true() << '\n' ; // true
}

http://coliru.stacked-crooked.com/a/ee460192c0c8b082
UPDATE: Actually, I take it back, the solution I thought I had is not a complete solution. The inverse (!) of formulas just returns a bool and not the actual inverse of the formula. JLBorges's solution is so far the only one I've found to work. The only downside to this solution is that it dynamically allocates the actual formulas, but I don't know how to solve it otherwise.

I've finally got a working solution. It's inspired by JLBorges answer, but doesn't use a pointer/reference variable in the abstract class. Instead, in the base class I defined and implemented the bool not and general operators and && and || operators for generating AndFormulas and OrFormulas and then used these two operators in the isTrue functions for these two types of Formula's to make the comparisons. I even managed to keep the abstractness of the base class.
Last edited on
Topic archived. No new replies allowed.