gcc optimizes code when I pass it the -O2 flag, but I'm wondering how well it can actually do that if I compile all source files to object files and then link them afterwards.
Here's an example:
// in a.h
int foo(int n);
// in foo.cpp
int foo(int n) {
return n;
}
// in main.cpp
#include "a.h"
int main(void) {
return foo(5);
}
// code used to compile it all
gcc -c -O2 foo.cpp -o foo.o
gcc -c -O2 main.cpp -o main.o
gcc -O2 foo.o main.o -o executable
Normally, gcc should inline foo because it's a small function and -O2 enables -finline-small-functions, right? But here, gcc only sees the code of foo and main independently before it creates the object files, so there won't be any optimizations like that, right? So, does compiling like this really make code slower?
However, I could also compile it like this:
gcc -O2 foo.cpp main.cpp -o executable
Would that be faster? If not, would it be faster this way?
// in foo.cpp
int foo(int n) {
return n;
}
// in main.cpp
#include "foo.cpp"
int main(void) {
return foo(5);
}
Edit: I looked at objdump, and its disassembled code showed that only the #include "foo.cpp" thing worked.
It seems that you have rediscovered on your own the issue about the separate compilation model that C and C++ use. While it certainly eases memory requirements (which was important at the time of its creation), it does so by exposing only minimal information to the compiler, meaning that some optimizations (like this one) cannot be performed.
Newer languages, with their module systems can expose as much information as necessary, and we can hope to rip those benefits if modules get into the next version of C++...
In the mean time, the simplest thing to go for is called Link-Time Optimization. The idea is that you will perform as much optimization as possible on each TU (Translation Unit) to obtain an object file, but you will also enrich the traditional object file (which contain assembly) with IR (Intermediate Representation, used by compilers to optimize) for part of or all functions.
When the linker will be invoked to merge those object files together, instead of just merging the files together, it will merge the IR representations, rexeecute a number of optimization passes (constant propagation, inlining, ...) and then create assembly on its own. It means that instead of being just a linker, it is in fact a backend optimizer.
Of course, like all optimization passes this has a cost, so makes for longer compilation. Also, it means that both the compiler and the linker should be passed a special option to trigger this behavior, in the case of gcc, it would be -lto or -O4.
You may be looking for Link-Time Optimization (LTO), aka Whole Program Optimization.
Since you're using GCC, you can use the C99 inline function specifier mechanism. This is from ISO/IEC 9899:1999.
§ 6.7.4 Function specifiers
Syntax
¶1 function-specifier:
inline
Constraints
¶2 Function specifiers shall be used only in the declaration of an identifier for a function.
¶3 An inline definition of a function with external linkage shall not contain a definition of a
modifiable object with static storage duration, and shall not contain a reference to an
identifier with internal linkage.
¶4 In a hosted environment, the inline function specifier shall not appear in a declaration
of main.
Semantics
¶5 A function declared with an inline function specifier is an inline function. The
function specifier may appear more than once; the behavior is the same as if it appeared
only once. Making a function an inline function suggests that calls to the function be as
fast as possible.118) The extent to which such suggestions are effective is
implementation-defined.119)
¶6 Any function with internal linkage can be an inline function. For a function with external
linkage, the following restrictions apply: If a function is declared with an inline
function specifier, then it shall also be defined in the same translation unit. If all of the
file scope declarations for a function in a translation unit include the inline function
specifier without extern, then the definition in that translation unit is an inline
definition. An inline definition does not provide an external definition for the function,
and does not forbid an external definition in another translation unit. An inline definition
provides an alternative to an external definition, which a translator may use to implement
any call to the function in the same translation unit. It is unspecified whether a call to the
function uses the inline definition or the external definition.120)
¶7 EXAMPLE The declaration of an inline function with external linkage can result in either an external
definition, or a definition available for use only within the translation unit. A file scope declaration with
extern creates an external definition. The following example shows an entire translation unit.
inline double fahr(double t)
{
return (9.0 * t) / 5.0 + 32.0;
}
inline double cels(double t)
{
return (5.0 * (t - 32.0)) / 9.0;
}
extern double fahr(double); // creates an external definition
double convert(int is_fahr, double temp)
{
/* A translator may perform inline substitutions */
return is_fahr ? cels(temp) : fahr(temp);
}
¶8 Note that the definition of fahr is an external definition because fahr is also declared with extern, but
the definition of cels is an inline definition. Because cels has external linkage and is referenced, an
external definition has to appear in another translation unit (see 6.9); the inline definition and the external
definition are distinct and either may be used for the call.
118) By using, for example, an alternative to the usual function call mechanism, such as "inline
substitution". Inline substitution is not textual substitution, nor does it create a new function.
Therefore, for example, the expansion of a macro used within the body of the function uses the
definition it had at the point the function body appears, and not where the function is called; and
identifiers refer to the declarations in scope where the body occurs. Likewise, the function has a
single address, regardless of the number of inline definitions that occur in addition to the external
definition.
119) For example, an implementation might never perform inline substitution, or might only perform inline
substitutions to calls in the scope of an inline declaration.
120) Since an inline definition is distinct from the corresponding external definition and from any other
corresponding inline definitions in other translation units, all corresponding objects with static storage
duration are also distinct in each of the definitions.
Note that GCC also had inline functions in C before they were standardized. Read the GCC manual for details if you need that notation.
Related
Ok, not a C/C++ expert by any means, but I thought the point of a header file was to declare the functions, then the C/CPP file was to define the implementation.
However, reviewing some C++ code tonight, I found this in a class's header file...
public:
UInt32 GetNumberChannels() const { return _numberChannels; } // <-- Huh??
private:
UInt32 _numberChannels;
So why is there an implementation in a header? Does it have to do with the const keyword? Does that inline a class method? What exactly is the benefit/point of doing it this way vs. defining the implementation in the CPP file?
Ok, not a C/C++ expert by any means, but I thought the point of a header file was to declare the functions, then the C/CPP file was to define the implementation.
The true purpose of a header file is to share code amongst multiple source files. It is commonly used to separate declarations from implementations for better code management, but that is not a requirement. It is possible to write code that does not rely on header files, and it is possible to write code that is made up of just header files (the STL and Boost libraries are good examples of that). Remember, when the preprocessor encounters an #include statement, it replaces the statement with the contents of the file being referenced, then the compiler only sees the completed pre-processed code.
So, for example, if you have the following files:
Foo.h:
#ifndef FooH
#define FooH
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
#endif
Foo.cpp:
#include "Foo.h"
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
#include "Foo.h"
Foo f;
UInt32 chans = f.GetNumberChannels();
The preprocessor parses Foo.cpp and Bar.cpp separately and produces the following code that the compiler then parses:
Foo.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
UInt32 Foo::GetNumberChannels() const
{
return _numberChannels;
}
Bar.cpp:
class Foo
{
public:
UInt32 GetNumberChannels() const;
private:
UInt32 _numberChannels;
};
Foo f;
UInt32 chans = f.GetNumberChannels();
Bar.cpp compiles into Bar.obj and contains a reference to call into Foo::GetNumberChannels(). Foo.cpp compiles into Foo.obj and contains the actual implementation of Foo::GetNumberChannels(). After compiling, the linker then matches up the .obj files and links them together to produce the final executable.
So why is there an implementation in a header?
By including the method implementation inside the method declaration, it is being implicitly declared as inlined (there is an actual inline keyword that can be explicitly used as well). Indicating that the compiler should inline a function is only a hint which does not guarantee that the function will actually get inlined. But if it does, then wherever the inlined function is called from, the contents of the function are copied directly into the call site, instead of generating a CALL statement to jump into the function and jump back to the caller upon exiting. The compiler can then take the surrounding code into account and optimize the copied code further, if possible.
Does it have to do with the const keyword?
No. The const keyword merely indicates to the compiler that the method will not alter the state of the object it is being called on at runtime.
What exactly is the benefit/point of doing it this way vs. defining the implementation in the CPP file?
When used effectively, it allows the compiler to usually produce faster and better optimized machine code.
It is perfectly valid to have an implementation of a function in a header file. The only issue with this is breaking the one-definition-rule. That is, if you include the header from multiple other files, you will get a compiler error.
However, there is one exception. If you declare a function to be inline, it is exempt from the one-definition-rule. This is what is happening here, since member functions defined inside a class definition are implicitly inline.
Inline itself is a hint to the compiler that a function may be a good candidate for inlining. That is, expanding any call to it into the definition of the function, rather than a simple function call. This is an optimization which trades the size of the generated file for faster code. In modern compilers, providing this inlining hint for a function is mostly ignored, except for the effects it has on the one-definition-rule. Also, a compiler is always free to inline any function it sees fit, even if it has not been declared inline (explicitly or implicitly).
In your example, the use of const after the argument list signals that the member function does not modify the object on which it is called. In practice, this means that the object pointed to by this, and by extension all class members, will be considered const. That is, trying to modify them will generate a compile-time error.
It is implicitly declared inline by virtue of being a member function defined within the class declaration. This does not mean the compiler has to inline it, but it means you won't break the one definition rule. It is completely unrelated to const*. It is also unrelated to the length and complexity of the function.
If it were a non-member function, then you would have to explicitly declare it as inline:
inline void foo() { std::cout << "foo!\n"; }
* See here for more on const at the end of a member function.
Even in plain C, it is possible to put code in a header file. If you do it, you usually need to declare it static or else multiple .c files including the same header will cause a "multiply defined function" error.
The preprocessor textually includes an include file, so the code in an include file becomes part of the source file (at least from the compiler's point of view).
The designers of C++ wanted to enable object-oriented programming with good data hiding, so they expected to see lots of getter and setter functions. They didn't want an unreasonable performance penalty. So, they designed C++ so that the getters and setters could not only be declared in the header but actually implemented, so they would inline. That function you showed is a getter, and when that C++ code is compiled, there won't be any function call; code to fetch out that value will just be compiled in place.
It is possible to make a computer language that doesn't have the header file/source file distinction, but just has actual "modules" that the compiler understands. (C++ didn't do that; they just built on top of the successful C model of source files and textually included header files.) If source files are modules, it would be possible for a compiler to pull code out of the module and then inline that code. But the way C++ did it is simpler to implement.
As far as I know, there are two kinds of methods, which can be safely implemented inside the header file.
Inline methods - their implementation is copied to places, where they are used, so there is no problem with double-definition linker errors;
Template methods - they are actually compiled at the moment of template instantiation (eg. when someone inputs a type in place of template), so again there is no possibility of double-definition problem.
I believe, your example fits the first case.
C++ standard quotes
The C++17 N4659 standard draft 10.1.6
"The inline specifier" says that methods are implicitly inline:
4 A function defined within a class definition is an inline function.
and then further down we see that inline methods not only can, but must be defined on all translation units:
6 An inline function or variable shall be defined in every translation unit in which it is odr-used and shall
have exactly the same definition in every case (6.2).
This is also explicitly mentioned in a note at 12.2.1 "Member functions":
1 A member function may be defined (11.4) in its class definition, in which case it is an inline member function (10.1.6) [...]
3 [ Note: There can be at most one definition of a non-inline member function in a program. There may be
more than one inline member function definition in a program. See 6.2 and 10.1.6. — end note ]
GCC 8.3 implementation
main.cpp
struct MyClass {
void myMethod() {}
};
int main() {
MyClass().myMethod();
}
Compile and view symbols:
g++ -c main.cpp
nm -C main.o
output:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
U __stack_chk_fail
0000000000000000 T main
then we see from man nm that the MyClass::myMethod symbol is marked as weak on the ELF object files, which implies that it can appear on multiple object files:
"W"
"w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked
and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error. On some systems, uppercase indicates that a default value has been specified.
Keeping the implementation in the class header file works, as I'm sure you know if you compiled your code. The const keyword ensures you don't change any members, it keeps the instance immutable for the duration of the method call.
I have these three files
// foo.h
#pragma once
template <typename T> const T foo;
template <>
const int foo<int> = 1;
// a.cpp
#include "foo.h"
int main() {}
// b.cpp
#include "foo.h"
If I build these with GCC 7.5.0 on Linux it works fine:
$ g++ -std=c++17 a.cpp b.cpp -o out
$
However with Apple Clang 12.0.0 on Mac it gives this error:
$ clang++ -std=c++17 a.cpp b.cpp -o out
duplicate symbol 'foo<int>' in:
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/a-62bdde.o
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/b-ea4997.o
ld: 1 duplicate symbol for architecture x86_64
Should this be an error, or is it a bug in Clang?
RedFrog's answer is correct - this violates the One Definition Rule (ODR) but it doesn't really explain why it violates the ODR. In C++ global const variables have internal (static) linkage, which should result in a separate instance of the variable for each compilation unit, so ODR isn't violated. However it turns out that const does not cause template variables to have internal linkage:
The const qualifier used on a declaration of a non-local non-volatile non-template (since C++14) non-inline (since C++17) variable that is not declared extern gives it internal linkage.
There are a few ways to fix this.
Internal Linkage using static
You can use static on template variables to gives them internal linkage:
Any of the following names declared at namespace scope have internal linkage:
variables, variable templates (since C++14), functions, or function templates declared static;
Edit: I don't recommend using this technique. If you apply static to the declaration and the specialisations then it works fine in Clang but GCC (as of now at least) complains that
error: explicit template specialization cannot have a storage class
If you set static only on the declaration, then it compiles in GCC but you get duplicate symbol errors with Clang.
Internal Linkage using namespace
Putting template variables in an anonymous namespace also gives them internal linkage.
In addition, all names declared in unnamed namespace or a namespace within an unnamed namespace, even ones explicitly declared extern, have internal linkage.
Disable ODR using inline
This is a bit different. Template variables that are declared inline but not static still have external linkage, but are allowed to have more than one definition.
An inline function or variable (since C++17) with external linkage (e.g. not declared static) has the following additional properties:
There may be more than one definition of an inline function or variable (since C++17) in the program as long as each definition appears in a different translation unit and (for non-static inline functions and variables (since C++17)) all definitions are identical. For example, an inline function or an inline variable (since C++17) may be defined in a header file that is #include'd in multiple source files.
It must be declared inline in every translation unit.
It has the same address in every translation unit.
This means that inline for variables behaves like inline for functions.
If in doubt, inline is probably the best option to go for. In the very simple tests I've done they all produced identical output, even at -O0, but in theory inline guarantees that there is only one copy of the variable in your program because it still has external linkage, whereas the other methods don't. Maybe.
As given, this is indeed an ODR violation as stated in the other answers, but the point here should be that this is a GCC bug. [basic.link]/3.2 gives internal linkage to a “non-template variable of non-volatile const-qualified type”, but GCC is giving foo internal linkage despite it being a variable template. (Here, we could rationalize it as a consequence of there being no diagnostic required, but the bug can be observed by a well-formed program by comparing the address of an implicitly instantiated specialization in two different translation units.)
it violates ODR, and should be inline (since C++17).
why it violates ODR:
One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.
because foo.h is included by both a.cpp and b.cpp, the variable foo<int> is defined in each translation unit, a.cpp and b.cpp. so it has 2 definitions in this program, which violates the former rule, except if it's unused (it means, not ODR-used) or it's an inline variable.
you have 2 solutions:
put template<> int const foo<int> = 1; into another foo.cpp. and then it will have only one definition.
add inline (since C++17) for foo. an inline variable is allowed to have more than one same definitions among translation units.
I have these three files
// foo.h
#pragma once
template <typename T> const T foo;
template <>
const int foo<int> = 1;
// a.cpp
#include "foo.h"
int main() {}
// b.cpp
#include "foo.h"
If I build these with GCC 7.5.0 on Linux it works fine:
$ g++ -std=c++17 a.cpp b.cpp -o out
$
However with Apple Clang 12.0.0 on Mac it gives this error:
$ clang++ -std=c++17 a.cpp b.cpp -o out
duplicate symbol 'foo<int>' in:
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/a-62bdde.o
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/b-ea4997.o
ld: 1 duplicate symbol for architecture x86_64
Should this be an error, or is it a bug in Clang?
RedFrog's answer is correct - this violates the One Definition Rule (ODR) but it doesn't really explain why it violates the ODR. In C++ global const variables have internal (static) linkage, which should result in a separate instance of the variable for each compilation unit, so ODR isn't violated. However it turns out that const does not cause template variables to have internal linkage:
The const qualifier used on a declaration of a non-local non-volatile non-template (since C++14) non-inline (since C++17) variable that is not declared extern gives it internal linkage.
There are a few ways to fix this.
Internal Linkage using static
You can use static on template variables to gives them internal linkage:
Any of the following names declared at namespace scope have internal linkage:
variables, variable templates (since C++14), functions, or function templates declared static;
Edit: I don't recommend using this technique. If you apply static to the declaration and the specialisations then it works fine in Clang but GCC (as of now at least) complains that
error: explicit template specialization cannot have a storage class
If you set static only on the declaration, then it compiles in GCC but you get duplicate symbol errors with Clang.
Internal Linkage using namespace
Putting template variables in an anonymous namespace also gives them internal linkage.
In addition, all names declared in unnamed namespace or a namespace within an unnamed namespace, even ones explicitly declared extern, have internal linkage.
Disable ODR using inline
This is a bit different. Template variables that are declared inline but not static still have external linkage, but are allowed to have more than one definition.
An inline function or variable (since C++17) with external linkage (e.g. not declared static) has the following additional properties:
There may be more than one definition of an inline function or variable (since C++17) in the program as long as each definition appears in a different translation unit and (for non-static inline functions and variables (since C++17)) all definitions are identical. For example, an inline function or an inline variable (since C++17) may be defined in a header file that is #include'd in multiple source files.
It must be declared inline in every translation unit.
It has the same address in every translation unit.
This means that inline for variables behaves like inline for functions.
If in doubt, inline is probably the best option to go for. In the very simple tests I've done they all produced identical output, even at -O0, but in theory inline guarantees that there is only one copy of the variable in your program because it still has external linkage, whereas the other methods don't. Maybe.
As given, this is indeed an ODR violation as stated in the other answers, but the point here should be that this is a GCC bug. [basic.link]/3.2 gives internal linkage to a “non-template variable of non-volatile const-qualified type”, but GCC is giving foo internal linkage despite it being a variable template. (Here, we could rationalize it as a consequence of there being no diagnostic required, but the bug can be observed by a well-formed program by comparing the address of an implicitly instantiated specialization in two different translation units.)
it violates ODR, and should be inline (since C++17).
why it violates ODR:
One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.
because foo.h is included by both a.cpp and b.cpp, the variable foo<int> is defined in each translation unit, a.cpp and b.cpp. so it has 2 definitions in this program, which violates the former rule, except if it's unused (it means, not ODR-used) or it's an inline variable.
you have 2 solutions:
put template<> int const foo<int> = 1; into another foo.cpp. and then it will have only one definition.
add inline (since C++17) for foo. an inline variable is allowed to have more than one same definitions among translation units.
When I declare a global variable in two different source files and only define it in one of the source files, I get different results compiling for C++ than for C. See the following example:
main.c
#include <stdio.h>
#include "func.h" // only contains declaration of void print();
int def_var = 10;
int main() {
printf("%d\n", def_var);
return 0;
}
func.c
#include <stdio.h>
#include "func.h"
/* extern */int def_var; // extern needed for C++ but not for C?
void print() {
printf("%d\n", def_var);
}
I compile with the following commands:
gcc/g++ -c main.c -o main.o
gcc/g++ -c func.c -o func.o
gcc/g++ main.o func.o -o main
g++/clang++ complain about multiple definition of def_var (this is the behaviour I expected, when not using extern).
gcc/clang compile just fine. (using gcc 7.3.1 and clang 5.0)
According to this link:
A tentative definition is a declaration that may or may not act as a definition. If an actual external definition is found earlier or later in the same translation unit, then the tentative definition just acts as a declaration.
So my variable def_var should be defined at the end of each translation unit and then result in multiple definitions (as it is done for C++). Why is that not the case when compiling with gcc/clang?
This isn't valid C either, strictly speaking. Says as much in
6.9 External definitions - p5
An external definition is an external declaration that is also a
definition of a function (other than an inline definition) or an
object. If an identifier declared with external linkage is used in an
expression (other than as part of the operand of a sizeof or _Alignof
operator whose result is an integer constant), somewhere in the entire
program there shall be exactly one external definition for the
identifier; otherwise, there shall be no more than one.
You have two definitions for an identifier with external linkage. You violate that requirement, the behavior is undefined. The program linking and working is not in opposition to that. It's not required to be diagnosed.
And it's worth noting that C++ is no different in that regard.
[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 outside of a
discarded statement; 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
or variable shall be defined in every translation unit in which it is
odr-used outside of a discarded statement.
Again, a "shall" requirement, and it says explicitly that no diagnostic is required. As you may have noticed, there's quite a bit more machinery that this paragraph can apply to. So the front ends for GCC and Clang probably need to work harder, and as such are able to diagnose it, despite not being required to.
The program is ill-formed either way.
As M.M pointed out in a comment, the C standard has an informative section that mentions the very extension in zwol's answer.
J.5.11 Multiple external definitions
There may be more than one external definition for the identifier of
an object, with or without the explicit use of the keyword extern; if
the definitions disagree, or more than one is initialized, the
behavior is undefined (6.9.2).
I believe you are observing an extension to C known as "common symbols", implemented by most, but not all, Unix-lineage C compilers, originally (IIUC) for compatibility with FORTRAN. The extension generalizes the "tentative definitions" rule described in StoryTeller's answer to multiple translation units. All external object definitions with the same name and no initializer,
int foo; // at file scope
are collapsed into one, even if they appear in more than one TU, and if there exists an external definition with an initializer for that name,
int foo = 1; // different TU, also file scope
then all of the external definitions with no initializers are treated as external declarations. C++ compilers do not implement this extension, because (oversimplifying) nobody wanted to figure out what it should do in the presence of templates. For GCC and Clang, you can disable the extension with -fno-common, but other Unix C compilers may not have any way to turn it off.
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;
}
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)
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.
Inline functions should have a definition in every translation unit where used and in your case there is no one in main.cpp
The definition of an inline function doesn't have to be in a header
file but, because of the one definition rule for inline functions, an
identical definition for the function must exist in every translation
unit that uses it.
The easiest way to achieve this is by putting the definition in a
header file.
If you want to put the definition of a function in a single source
file then you shouldn't declare it inline. A function not declared
inline does not mean that the compiler cannot inline the function.
By #CharlesBailey
1 Does either compiler have conforming behavior in this case?
From this standard citation we can say that in this specific case VC is right.
C++ Standard, §3.2 One definition rule / 3: An inline function shall
be defined in every translation unit in which it is odr-used
thanks #Pixelchemist
2 Why does VC++ work if the method is non-virtual?
Accoring to the standard non-virtual functions are not "One Definition Rule" - used, so it allows to use it the way you want.