Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking an inline virtual method

Tags:

c++

GCC 4.7.2 compiles this code as follows:

  • The reference to Obj::getNumber() in Obj::defaultNumber() is inlined
  • The body of Obj::getNumber() is separately compiled and exported and can be linked to from a different translation unit.

VC++2012 fails at the link step with:

  • Error 1 error LNK2001: unresolved external symbol "public: virtual int __thiscall Obj::getNumber(unsigned int)" in main.obj

VC++ seems to be inlining the call within Obj::defaultNumber() but not exporting getNumber() in the symbol table.

VC++ can be made to compile it by one of the following:

  • Remove the inline keyword from the getNumber() definition, or
  • Remove the virtual keyword from the getNumber() declaration (Why!?)

At first glance, GCC's behavior certainly seems more helpful/intuitive. Perhaps someone familiar with the Standard could point me in the right direction here.

  • Does either compiler have conforming behavior in this case?
  • Why does VC++ work if the method is non-virtual?

.

// Obj.h

class Obj
{
public:
    virtual int getNumber(unsigned int i);
    virtual int defaultNumber();
};

// Obj.cpp

static const int int_table[2] = { -1, 1 };

inline int Obj::getNumber(unsigned int i)
{
    return int_table[i];
}

int Obj::defaultNumber()
{
    return getNumber(0);
}

// main.cpp

#include <iostream>
#include "Obj.h"

int main()
{
    Obj obj;
    std::cout << "getNumber(1): " << obj.getNumber(1) << std::endl;
    std::cout << "defaultNumber(): " << obj.defaultNumber() << std::endl;
}
like image 770
Unsigned Avatar asked Jul 08 '13 21:07

Unsigned


People also ask

Can you inline a virtual function?

Whenever a virtual function is called using base class reference or pointer it cannot be inlined, but whenever called using the object without reference or pointer of that class, can be inlined because the compiler knows the exact class of the object at compile time.

What do you mean by inline method?

An inline function is one for which the compiler copies the code from the function definition directly into the code of the calling function rather than creating a separate set of instructions in memory. This eliminates call-linkage overhead and can expose significant optimization opportunities.

What is inline method in Java?

If a function is inline, the compiler places a copy of the code of that function at each point where the function is called at compile time.


2 Answers

I changed the answer completely once, why not twice? It's quite late sorry for any typos.

Short

Does either compiler have conforming behavior in this case?

Both of them do. It's undefined behaviour.

Why does VC++ work if the method is non-virtual?

Because your code is non-standard conformant and the compiler can construe your code at its own discretion.

Long

Why are the compilers conformant?

The standard says that a virtual member is used per one-definition rule if not pure and that every translation unit needs its own definition of an odr-used inline function.

§ 3.2

2) "[...] A virtual member function is odr-used if it is not pure. [...]"
3) "[...] An inline function shall be defined in every translation unit in which it is odr-used. [...]"

§ 7.1.2

4) An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2). [...]

Additionally the standard requires the function to be declared inline within each translation unit but allows the compilers to omit any diagnostics.

§ 7.1.2

4) [...] If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. [...]

Since your getNumber() isn't declared inline in both main.cpp and Obj.cpp you're in the Land of undefined behaviour here.

(edit) I interpret this (see standard quotes below) in a way that the compiler is not required to check whether the function is declared inline everywhere it appears. I don't know but suspect that this "compilers don't have to check"-rule doesn't refer to the sentence "An inline function shall be defined in every translation unit in which it is odr-used[...]".

MSVC compiles even if getNumber is additionally defined in main (without ever being declared inline from main's point of view), even if the Obj.cpp implementation is still declared inline and present in the symbol table. This behaviour of the compiler is allowed by the standard even though the code is non-conformant: undefined behaviour (UB).(/edit)

Both compilers are therefore free to either accept and compile or reject your code.

Why does VC++ work if the method is non-virtual?

The question here is: Why doesn't VC++ instantiate a the virtual inline function but does so if virtual is left out?

You probably read those statements all around SO where it says "UB may blow your house, kill your mum, end the world" etc. I have no clue why there's no function instance if the function is virtual but otherwise there is. I guess that's what UB is about - weird things.

Why do both GCC and VS (without virtual) provide externally linked function instances of an inline function?

The compilers are not required to actually substitute the functions inline, they may well produce an instance of the function. Since inline functions have external linkage by default, this results in the possibility to call getNumber from main if there is an instance created.

  • The compiler will just add an undefined external into the main.obj (no problem, it's not inline here).
  • The linker will find the very same external symbol in the Obj.obj and link happily.

7.1.2

2) An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected.

4) An inline function with external linkage shall have the same address in all translation units.

§ 9.3

3) Member functions of a class in namespace scope have external linkage.

MSDN inline, __inline, __forceinline

The inline keyword tells the compiler that inline expansion is preferred. However, the compiler can create a separate instance of the function (instantiate) and create standard calling linkages instead of inserting the code inline. Two cases where this can happen are:

  • Recursive functions.
  • Functions that are referred to through a pointer elsewhere in the translation unit.

These reasons may interfere with inlining, as may others, at the discretion of the compiler; you should not depend on the inline specifier to cause a function to be inlined.

So probably there is one of the "other" reasons that makes the compiler instantiate it and create a symbol that is used by main.obj but I can't tell why that reason disappears if the function is virtual.

Conclusion:

The compiler may or may not instantiate an inline function and it may or may not accept the same function being inline in one and non-inline in another translation unit. To have your behaviour defined you need to

  • Have every declaration of your function inline or have all of them non-inline
  • Provide a separate definition of an inline function for every translation unit (i.e. inline definition in the header or separate definitions in the source files)
like image 175
Pixelchemist Avatar answered Oct 21 '22 21:10

Pixelchemist


Both compilers are correct. You are invoking undefined behavior for which no diagnostic is required per 7.1.2 para 2:

An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2). ... If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required.

GCC is free to do what it does and give you a program that just might happen to work. VC++ is equally valid in rejecting your code.

like image 29
David Hammen Avatar answered Oct 21 '22 21:10

David Hammen