Palace Chan
Palace Chan

Reputation: 9183

Why does the linker give me an error in the virtual case but not in the non-virtual case?

When I have something like this:

class A
{
  virtual void rat();
};

class B : public A
{
  virtual void rat() { ; } //implemented!
};

int main(int argc, char **argv)
{
  A *a = new B;
  delete a;
}

I get linker errors:

unless I make the base rat a pure virtual.

However, when I have this:

class A
{
  public:
  void rat();
};

int main(int argc, char **argv)
{
  A a;
}

This compiles fine and doesn't give me an undefined reference link error unless I explicitly try to call the rat function in my main (a.rat();). What is the rule on unimplemented base class virtual functions which are, however, implemented in a derived class as in the first failing code snippet?

Upvotes: 3

Views: 781

Answers (5)

Adam Rosenfield
Adam Rosenfield

Reputation: 400314

Because the C++ standard requires it to be implemented. From C++03 §10.3/8:

A virtual function declared in a class shall be defined, or declared pure (10.4) in that class, or both; but no diagnostic is required (3.2).

So you need to either declare it pure (with the = 0 suffix after the right parenthesis) or define its implementation.

As for why you don't get an error when you don't call the function in the non-virtual case, refer to C++03 §3.2/2-3 (emphasis mine)

2) An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19), is the operand of the sizeof operator (5.3.3), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8). An object or non-overloaded function is used if its name appears in a potentially-evaluated expression. A virtual member function is used if it is not pure. [...]

3) Every program shall contain exactly one definition of every non-inline function or object that is 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 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is used.

So, in the non-virtual case, if it's not used, a definition is not required. But in the non-pure virtual case, even if it's not explicitly referenced in code, it's still considered to be used by the standard, so its definition is required.

See also A virtual member function is used if it is not pure?.

Upvotes: 2

Mahesh
Mahesh

Reputation: 34625

Every non-pure virtual function needs to be implemented.

class A 
{ 
   public: void rat(); 
};

int main(int argc, char **argv) 
{ 
  A a; 
}

The above code is altogether a different scenario. In a situation where you don't call a non-pure virtual function, you neither get a compiler/linker error because you are not calling it at all despite it's declaration in class definition.

Now in the code below, compiler just checks whether there is a member function named rat() or not in A.

A a;
a.rat();   // Compiler passes. But linker bombs.

Now you should get a linker error.

Upvotes: 4

Matthieu M.
Matthieu M.

Reputation: 299930

The Standard legalese is ODR-used: a function that is ODR-used must be defined, otherwise it is an error.

The rules for marking a function as ODR-used are quite complicated, basically it means that the function is used in some way. In your second example, the function is not used, so it is unnecessary.

There is one special caveat: a virtual function (unless it is pure) is always considered ODR-used.

Upvotes: 2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726639

When both classes define virtual functions, C++ compiler needs to build vtables for both classes A and B. To build A's vtable, the compiler needs A::rat() - that is where the reference is coming from.

When A has no virtual functions, there are no reference to A::rat from anywhere, hence you do not get a compile error.

As I'm sure you know, you can fix this error by making A::rat a pure virtual, thus providing the needed value for the vtable (in this case, the value is zero).

Upvotes: 6

John Dibling
John Dibling

Reputation: 101456

Because A::rat() is not pure virtual, it must have an implementation.

Upvotes: 2

Related Questions