0

I have a class Literal which is really just a wrapper for (const int). I want to have a second class PositiveLiteral which inherits from Literal, but has a constructor that asserts that its value is positive.

class Literal {
public:
    Literal(int x):x(x){}
    virtual ~Literal(){}
    // Other methods
private:
    const int x;
}

class PositiveLiteral : public Literal {
public:
    PositiveLiteral(int x):Literal(x) {
        assert(x > 0)
    }
}

In this way, functions that expect a positive literal can simply take a PositiveLiteral as an argument. Then I don't need to put explicit assertions in my code and furthermore, where those assertions would fail, I can immediately see why.

I don't expect to otherwise inherit from Literal except for in this one case. Yet, because there is inheritance, I have to give Literal a virtual destructor to avoid undefined behavior which seems silly because PositiveLiteral has no exra information associated to it that Literal does not have. It's just a way to maintain an assertion without having to make it explicit.

What's another way to accomplish the same task without the need for a virtual method in what's supposed to be a simple wrapper class?

dspyz
  • 5,280
  • 2
  • 25
  • 63

1 Answers1

1

You don't need to have to have a virtual destructor unless you do dynamic allocation and delete via a pointer to base class.


The real problem lies at the design level. For while it is true that every PositiveLiteral value is a Literal value, if you have a reference to a Literal which is really a PositiveLiteral, then you can assign it a negative value…

In the literature and forum discussions this was once known as the ellipse-versus-circle problem, although the similarity isn't obvious.

First, to be very clear about the problem, it's only for immutable values that a PositiveLiteral is a Literal. It is not the case that a mutable PositiveLiteral is a mutable Literal.

Then, a practical C++ solution is to provide value conversion instead of using inheritance.

For example, this is the solution used for smart pointers.


Addendum: I failed to see that in the OP's code the value is const. So there's no such problem.

A practical problem is that at least one compiler, Visual C++, has a tendency to silly-warn about its inability to generate a copy assignment operator; it can be shut up by declaring a private one without implementation. :-)

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Thank you, I wasn't entirely sure about that (if I understand correctly, you're saying that even in the presence of polymorphism, I don't need a virtual destructor if the default destructor will suffice). Note that x is const in my Literal class, so the values are indeed immutable. That said, what do you mean by "value conversion"? I googled "C++ value conversion" and none of the first couple links seem to apply here. – dspyz Dec 11 '13 at 06:13
  • @dspyz: oh, I didn't see that it was `const`. second "failed to see it" this morning on SO. I definitely need more coffee! – Cheers and hth. - Alf Dec 11 '13 at 06:21
  • value conversion is where you have e.g. `operator Literal() const` in the `PositiveLiteral` class, to provide implicit conversion to `Literal`. It can also be expressed via a `Literal` constructor taking `PositiveLiteral` argument. – Cheers and hth. - Alf Dec 11 '13 at 06:22
  • Actually, can you confirm for me: I'm looking at both this SO http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors and this post which the accepted answer points to: http://www.gotw.ca/publications/mill18.htm and neither one mentions that you don't need a virtual destructor when the default destructor suffices. Have they both overlooked that? – dspyz Dec 11 '13 at 06:23
  • no, they haven't overlooked. both references say the same as i do above. however, while you can generally trust what Herb Sutter says (he is after all the chairman of the international C++ standardization committee, as well as chief architect of Visual C++ compiler), in my experience about half of SO answers are plausible but technically wrong, so that SO as authority isn't a good idea... – Cheers and hth. - Alf Dec 11 '13 at 06:28
  • Ok, but what if I might potentially delete via a base class reference. Then what can I do? It still seems like there shouldn't be a need for the virtual dtor because PositiveLiteral has no extra fields – dspyz Dec 11 '13 at 06:32
  • Also, I did misunderstand. What Cheers said was correct, but my interpretation was wrong. It has nothing to do with default destructor or not. It has to do with whether the instances are polymorphically deleted (this comment is for the benefit of anyone reading this discussion). – dspyz Dec 11 '13 at 06:35
  • 1
    Whatever code that deletes via a bass class reference when the base class doesn't have a virtual destructor, and object is of a derived class, is at fault. Not the class itself. For example, deriving from `std::string` and deleting via a pointer to `std::string`: that's the deleting code that is at fault, not `std::string`. :-) Of course it doesn't make much sense to allocate a `std::string` dynamically, so it's not designed for that (don't pay for what you don't use principle). And I think it's the same for your class. – Cheers and hth. - Alf Dec 11 '13 at 06:41
  • That makes a lot of sense. Thanks. I've known C++ syntax for a while and I understand how C++ constructs work, but as far as design goes, I still get tripped up on the proper way to do the simplest things. I started reading Effective C++ today and it's answered a lot of my questions. But coming from Java, I'm still somewhat uncomfortable with undefined behavior and I'm afraid to do anything that "passes the blame". – dspyz Dec 11 '13 at 06:56