2

To validate the statement "compiler & linker enforce existence of function body for pure virtual destructor." from this geeksforgeeks article, I compiled this code:

class Base
{
public:
    virtual ~Base()=0; // Pure virtual destructor
};

class Derived : public Base
{
public:
    ~Derived()
    {
        std::cout << "~Derived() is executed";
    }
};

int main()
{
    //Derived d;   <<<
    return 0;
}

which compiled without any error. So why the compiler didn't chose to enforce the existence of the function body in this case?

Wolf
  • 9,679
  • 7
  • 62
  • 108
Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
  • 1
    It compiled with and without the commented line, didn't it? – Yunnosch Aug 31 '17 at 07:00
  • What compiler/version and what compilation flags are you using? – fefe Aug 31 '17 at 07:00
  • @fefe Should that matter? `c++11 -Wall -Wextra ` will not give you any warning – Saurav Sahu Aug 31 '17 at 07:06
  • I think that compilers seldom judge about code completeness. If there is an implementation or not is clearly a linker job, see [my answer](https://stackoverflow.com/a/45975521/2932052) – Wolf Aug 31 '17 at 08:00
  • 1
    This question and the answers don't make sense if you (like me) don't know that pure virtual functions can have a definition and that pure virtual destructors should have one. Here is a great article by Herb Sutter http://www.gotw.ca/gotw/031.htm and a SO post https://stackoverflow.com/questions/630950/pure-virtual-destructor-in-c – bolov Aug 31 '17 at 09:06
  • @SauravSahu - an "undefined reference to Base::~Base()` error is not a runtime error, not is it a compilation error. It is a linker error. Yes, the pure virtual destructor must be defined since destructing an instance of a derived class involves calling destructors of all base classes. The only potential exception is if your `Base` has no concrete derived classes, or no derived classes are instantiated - in which case, there is no need to call the destructor (potential, because it depends on what your compiler & linker,in combination, do in that situation). And little point in base class – Peter Aug 31 '17 at 11:17
  • @Peter yes I realized that later. – Saurav Sahu Aug 31 '17 at 11:18

2 Answers2

10

Because the compiler (the entire translation process, actually) doesn't have to enforce anything if you perform an ODR1 violation. According to the C++ standard at [basic.def.odr/4]:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see [class.ctor], [class.dtor] and [class.copy]). An inline function shall be defined in every translation unit in which it is odr-used.

The compiler is perfectly within its right to figure out your program isn't actually using2 the destructor of Derived (and therefore the destructor of Base), and just not bother with notifying you.


1 One Definition Rule
2 What does it mean to “ODR-use” something?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • odr-used means? – Saurav Sahu Aug 31 '17 at 07:12
  • 2
    In what sense is it a ODR violation? I just see a missing destructor in the base class. Isn't this a linker issue at all? – Wolf Aug 31 '17 at 07:13
  • 1
    @SauravSahu - A standardese term (that all devs better know) which means that the object is used in a way that requires it to be defined once. – StoryTeller - Unslander Monica Aug 31 '17 at 07:13
  • @Wolf - The destructor of `Derived` needs the destructor of `Base`. That's what ODR uses it. – StoryTeller - Unslander Monica Aug 31 '17 at 07:14
  • https://stackoverflow.com/questions/19630570/what-does-it-mean-to-odr-use-something – Dan Aug 31 '17 at 07:14
  • 1
    @SauravSahu - No, I meant exactly what I said. The compiler doesn't have to inform you if you don't uphold the ODR. If you **do an ODR violation** no diagnostic is required. – StoryTeller - Unslander Monica Aug 31 '17 at 07:17
  • Uncommenting the object creation code gives a runtime error, not a compilation error. Doesn't that leave your last statement (about compiler's way of selecting scenario when to emit error) irrelevant, as compiler fails to pick the error in the case of usage as well? – Saurav Sahu Aug 31 '17 at 07:26
  • @SauravSahu - It's a linker error on my end (not a run-time error). And "not required" doesn't mean the translation process isn't allowed to issue a diagnostic. It only means it doesn't have to. In the case of uncommenting the line, it chose to do issue an error (because now it couldn't finish the build into a proper executable most probably). – StoryTeller - Unslander Monica Aug 31 '17 at 07:28
  • @SauravSahu - I think what confuses is that you somewhat artificially consider the translation process to be only "compilation". It isn't. It's the whole thing. – StoryTeller - Unslander Monica Aug 31 '17 at 07:31
  • Isn't also `Derived::~Derived` code contained in the object file? I think it's a question about compilation units and linking (and optimization). – Wolf Aug 31 '17 at 07:52
  • @Wolf - It's a language-lawyer question. And the fact the code compiles into an executable means it isn't simply about translation units. You get an executable after **linking**, after all. – StoryTeller - Unslander Monica Aug 31 '17 at 07:54
  • I think that the question shows a misconception about the role of the compiler in the whole translation process. Your answer is a bit misleading because **the code (with the commented-out `Derived` usage) actually doesn't perform an ODR violation**. Although [basic.def.odr/4](https://timsong-cpp.github.io/cppwp/n4140/basic.def.odr#4) is of course informative and also says something about the co-working of compiler and linker. That's why I added a [bridge for beginners](https://stackoverflow.com/a/45975521/2932052). – Wolf Aug 31 '17 at 08:15
  • @Wolf - You are wrong I'm afraid. It very much does perform an ODR violation. The definition of `~Derived` is present, translated and [**ODR-uses**](https://timsong-cpp.github.io/cppwp/n4140/class.dtor#8) `~Base`. The fact the violation doesn't hinder the generation of the complete executable, since non of this is called anyway, is why there is no diagnostic. – StoryTeller - Unslander Monica Aug 31 '17 at 08:24
  • You are right, I have learned something about ODR and ODR-use now. I (rhetorically) asked about `Derive::~Derived` in a former comment. But it's really hard to get the puzzle parts together (remember: the question was about the compiler enforcing something or not)? Most programmers aren't aware how ignorant compilers are about source code layout (whether `#include` is being used or not). The code (as shown in the code box) violates the ODR, as I understand now. Your answer is correct, but maybe a bit too high-level for most people. And now I have to rethink my "bridge" thoroughly. – Wolf Aug 31 '17 at 08:57
  • @Wolf - High Level? Maybe. I confess I fall into the trap of putting too much "standardese" and too little of my own words in my answers sometimes. But the OP mentioned reading something on *geeksforgees* (a bad resource IMO) and tagged with the language-lawyer tag to understand better. I tried to answer in the context of "the law" alone. – StoryTeller - Unslander Monica Aug 31 '17 at 09:00
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153337/discussion-between-wolf-and-storyteller). – Wolf Aug 31 '17 at 09:23
1

Does compiler really enforce the implementation of the pure virtual destructor?

No the compiler does nothing like this.

The compiler compiles compilation units to object files, that's why it

compiled without any error.

I think nearly every compiler will compile this without any error. But the linker will complain. The compiler just adds code to object files, and also in- and outgoing references that are bound by the linker (for static linking).

Of course the program will not link if you comment-in the line Derived d; again, see online demo.

Update

What you show in your question is just a single compilation unit, if you link it as a program, the linker will probably remove unused code. StorryTeller's answer says a lot about this.

If you comment-in the Derived usage in main and copy the definition of Base class into another compilation unit and add the destructor implementation there, you'll see that both will be linked together and the resulting program will run without any error. The compiler itself doesn't care if you include definitions from headers or not. I don't suggest to do this for productive programming but to understand why the compiler traditionally doesn't care about completeness of definitions. Most real-world compilation units are often incomplete.

Wolf
  • 9,679
  • 7
  • 62
  • 108