Question

The One Definition Rule states that:
In the entire program, an object or non-inline function cannot have more than one definition. (from Wikipedia)

Well, I know that if an member function is defined in a header file, it's implicitly inlined, and it's ok with the ODR.

But what about virtual functions? We know that if an virtual function is called polymorphically, it can't be inlined. if such a virtual function is deined in a header file, will that violate the ODR ?

For instance:

//derived.hpp
#include <iostream>
class Base {
public:
  virtual ~Base() {}
  virtual void vfunc() {
    std::cout << "Base::vfunc()\n";
  }
};

class Derived : public Base {
public:
  virtual ~Derived() {}

  virtual void vfunc() {
    std::cout << "Derived::vfunc()\n";
  }
};

//foo.cpp

#include "derived.hpp"
void func() {
  Base* ptr = new Derived();
  ptr->vfunc(); //polymorphic call, can't be inlined
  delete ptr;

  ptr = new Base();
  ptr->vfunc();
  delete ptr;
}
//main.cpp

#include "derived.hpp"

int main() {
  Base* ptr = new Derived();
  ptr->vfunc(); //polymorphic call, can't be inlined
  delete ptr;

  ptr = new Base();
  ptr->vfunc();
  delete ptr;
  return 0;
}

I'm curious about:

vfunc(and dtor) is called in both foo.cpp and main.cpp polymorphically(not inlined), which means it's defined twice in the entire program, so it violates the ODR, isn't it? How does it compiles(link) ?

I have just seen that:

More than one definition

In certain cases, there can be more than one definition of a type or a template. A program consisting of multiple header files and source files will typically have more than one definition of a type, but not more than one definition per translation unit. If a program contains more than one definition of a type, then each definition must be equivalent (also taken from Wikipedia)

What are the certain cases? Whether the above case be one of it?

Was it helpful?

Solution

vfunc(and dtor) is called in both foo.cpp and main.cpp polymorphically(not inlined), which means it's defined twice in the entire program,

first of all: calling does not mean definition. So the function calls do not tell you wether the ODR is violated.

so it violates the ODR, isn't it? How does it compiles(link) ?

It compiles, because member functions defined inside a class definition are implicitly declared inline and therefore don't violate the ODR. That applies for both vfunc definitions as well as for the dtors, so you are good here.

Note: There is a difference between an function declared inline (explicitly or implicitly) and a function that actually gets inlined. The compiler's decision to inline a function once could be influenced by the inline keyword, but it was and is only a hint. These days optimizers can predict better than any human when and when not inlining is a good choice, so the compiler can ignore that hint whenever it sees fit, and it can inline functions that have not been declared inline. So with modern compilers, inline is just a means to obey the ODR for functions, that are not declared inline implicitly.

Update: So in your case, the function is declared inline implicitly, the definition is included in two translation units (the two .cpp's) and it does not get inlined by the compiler. In this case, the linker will see the symbol for the function twice, but it will not complain about multiple symbols because of the inline declaration.

OTHER TIPS

By defining the function inside the class declaration, it is a inline declared function. The fact that the the compiler may not be able to inline it (except in some circumstances) doesn't alter the inline declaration fact. It is also true, that your definition is the same in each case (unless you use macros to alter the contents of the function, e.g. cout isn't defined the same way in both cases, or something like that).

If, on the other hand we have something like this in the headerfile:

class Base {
public:
  virtual ~Base() {}
  virtual void vfunc();
};

class Derived : public Base {
public:
  virtual ~Derived() {}

  virtual void vfunc();
};



void Base::vfunc()
{
  {
    std::cout << "Base::vfunc()\n";
  }
}

void Derived::vfunc()
{
  {
    std::cout << "Derived::vfunc()\n";
  }
}

Now you are breaking ODR, since Derived::vfunc() is not declared inline, and by being included multiple times, it gets defined more than once (albeit with exactly the same definition).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top