inline function choose criteria - c++

I have the following:
//a.cpp
inline int f(int x) { return x; }
int g(int x) { return f(x); }
//b.cpp
#include <iostream>
inline int f(int x) { return x + 1; }
extern int g(int);
int main() {
std::cout << g(2) << f(2) << std::endl;
}
The output is 22 (MSVC and GCC), that is, a.cpp f function is been inlined instead of b.cpp one. What's the criteria to choose the f function in this case?

Your code is ill-formed, no diagnostic required. All inline function definitions need to be the same in all translations units.
Since you do not do that the code is ill-formed, but it is allowed to compile and it is not required to cause any sort of warning or error.

You break ODR (One Definition Rule) here and have ill formed program, no diagnostic required.
Each inline definition should be identical.

Related

ODR violations - gcc

Are odr violations allowed with default gcc( g++, 5.4) options?
For e.g. following code
ODRTest.h
static int x = 0;
inline void odr_violates() {
++x;
std::cout << x << "\n";
}
void g();
void f();
ODRTest1.cpp
#include "ODRTest.h"
void g() { odr_violates();}
ODRTest2.cpp
#include "ODRTest.h"
void f() { odr_violates() ;}
main.cpp
#include "ODRTest.h"
int main() {
f();
g();
}
As x is static, the inline function odr_violates end up changing the static x defined in ODRTest1.cpp and ODRTest2.cpp. Isn't this a violation of ODR - one declaration and two definitions?
Following code also compiles
main.cpp
extern int q;
int main() {
std::cout << q << "\n";
}
ODRTest3.cpp
double q = 1.5;
q always gets printed as 0, is this usage an UB?
Is there an option in GCC that warns or errors out on these obvious ODR violations?
Thanks,
DDG

Why does defining functions in header files create multiple definition errors, but not classes?

This question builds off these two stackoverflow posts:
multiple definition in header file
Multiple definition of namespace function
Here's the question: Why doesn't the multiple definition error appear for classes/structs/enums? Why does it only apply to functions or variables?
I wrote some example code in an effort to capture my confusion. There are 4 files: namespace.h, test.h, test.cpp, and main.cpp. The first file is included in both test.cpp and main.cpp, which leads to the multiple definition error if the correct lines are uncommented.
// namespace.h
#ifndef NAMESPACE_H
#define NAMESPACE_H
namespace NamespaceTest {
// 1. Function in namespace: must be declaration, not defintion
int test(); // GOOD
// int test() { // BAD
// return 5;
//}
// 2. Classes can live in header file with full implementation
// But if the function is defined outside of the struct, it causes compiler error
struct TestStruct {
int x;
int test() { return 10; } // GOOD
};
//int TestStruct::test() { // BAD
// return 10;
//}
// 3. Variables are also not spared from the multiple definition error.
//int x = 20; // BAD
// 4. But enums are perfectly safe.
enum TestEnum { ONE, TWO }; // GOOD
}
#endif
// test.h
#ifndef TEST_H
#define TEST_H
class Test {
public:
int test();
};
#endif
// test.cpp
#include "test.h"
#include "namespace.h"
int NamespaceTest::test() {
return 5;
}
int Test::test() {
return NamespaceTest::test() + 1;
}
// main.cpp
#include <iostream>
#include "namespace.h"
#include "test.h"
int main() {
std::cout << "NamespaceTest::test: " << NamespaceTest::test() << std::endl;
Test test;
std::cout << "Test::test: " <<test.test() << std::endl;
NamespaceTest::TestStruct test2;
std::cout << "NamespaceTest::TestStruct::test: " << test2.test() << std::endl;
std::cout << "NamespaceTest::x: " << NamespaceTest::TestEnum::ONE << std::endl;
}
g++ test.cpp main.cpp -o main.out && ./main.out
NamespaceTest::test: 5
Test::test: 6
NamespaceTest::TestStruct::test: 10
NamespaceTest::x: 0
After reading cppreference: inline specifier, I have a partial answer. The rules for inline stipulate that functions defined within classes are considered inline. And inline functions are permitted to have duplicate definitions provided (1) they live in separate translation units and (2) are identical. I'm paraphrasing, but that's the gist.
That explains why the functions are legal, but not why multiple definitions of the class or enum are ok. Likely a similar explanation I imagine, but it would be good to know for sure.
Generally when you compile a definition that is namespace scoped (like functions or global variables), your compiler will emit a global symbol for it. If this appears in multiple translation units, there will be a conflict during link-time since there are multiple definitions (which happen to be equivalent, but the linker can't check this).
This is part of the one definition rule: Exactly one definition of a function or variable is allowed in the entire program, in one of the translation units.
There are some exceptions to this, for example, class definitions and inline functions/variables. However, definitions must be the exact same (textually) in all the translation units they appear in. Class definitions are meant to be #included, so it makes sense to allow them to appear in multiple translation units.
If you define a member function inside the class body they are implicitly inline because otherwise you would not be able to include the class definition with the member function definition without breaking ODR. For example, these three are functionally equivalent:
struct TestStruct {
int x;
int test() { return 10; }
};
// Could have been written
struct TestStruct {
int x;
inline int test() { return 10; }
};
// Or as
struct TestStruct {
int x;
int test(); // The `inline` specifier could also be here
};
inline int TestStruct::test() { return 10; }
You can do this to your namespace scoped functions/variables too: inline int test() { return 5; } and inline int x = 20; would have compiled with no further issue.
This is implemented by the compiler emitting "specially marked" symbols for inline entities, and the linker picking one arbitrarily since they should all be the same.
The same exception to ODR also exists for templated functions / variables and enum declarations, since they are also meant to live in header files.

static inlined function with static variables

After reading static variables in an inlined function, I wrote this test program:
main.cpp:
#include <iostream>
#include "a.h"
#include "f.h"
void g();
int main()
{
std::cout << "int main()\n";
f().info();
g();
}
a.h:
struct A
{
A() { std::cout << "A::A()\n"; }
void info() { std::cout << this << '\n'; }
};
f.h: (singleton local to each compilation unit because of the unnamed namespace)
namespace {
inline A& f()
{
static A x;
return x;
}
}
g.cpp:
#include <iostream>
#include "a.h"
#include "f.h"
void g()
{
std::cout << "void g()\n";
f().info();
}
The problem is that I don't get the same results with different compilers:
g++ 4.8.2: OK
int main()
A::A()
0x6014e8
void g()
A::A()
0x6014d0
clang++ 3.7.0: OK
int main()
A::A()
0x6015d1
void g()
A::A()
0x6015c1
icpc 15.0.2: no call to A::A() inside g() !
int main()
A::A()
0x601624
void g()
0x601620
Is this a bug in icpc ? Is the behaviour of the program not defined ? If I replace "namespace {...}" with "static" in f.h, A::A() is called inside g() as expected. Shouldn't the behaviour be the same?
If I remove "namespace {...}" in f.h, A::info() prints the same object address in main() and g(), and A::A() is called only once (with all compilers) as expected.
Since C++11 mandates names in unnamed namespaces to have internal linkage, every translation unit should have it's own version of f() and, as a result, it's own version of static A x. So gcc and clang are correct.
Seems like icpc is not following C++11 standard and instead following C++03, which allowed those names to be declared with external linkage. Since when you explicitly make f() internal by using static icpc follows the suit, I have a strong suspicion this is simply a bug.

C++ inline function and external function with the same name give an unexpected result

Following code do not violate One Definition Rule, yet it is giving an unexpected result:
Test.hpp
class Test
{
public:
int test();
};
Test1.cpp
#include "Test.hpp"
int Test::test()
{
return 1;
}
int test1() // expected to return 1
{
Test a = Test();
return a.test();
}
Test2.cpp
#include "Test.hpp"
inline int Test::test() // doesn't violate ODR
{
return 99;
}
int test2() // expected to return 99
{
Test a = Test();
return a.test();
}
main.cpp
#include <iostream>
int test1();
int test2();
int main()
{
std::cout << test1() << std::endl;
std::cout << test2() << std::endl;
}
I am expecting it to print "1 99", but it always prints "1 1".
Regarding two definitions of Test::test, since one of them is an inline definition, it does not violate One Definition Rule as well.
So this program is valid, but it is not printing out expected result...
Is there anything wrong with this program? Or am I misunderstanding something about ODR rule? (a reference to C++ standard would be helpful).
You are not allowed to define the function as both inline and non-inline.
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.
([dcl.fct.spec]/4)

Is my understanding about [basic.link]/7 in N4140 correct?

VS2015 compiles and executes the following snippet without a problem. g++ and clang don't link the code, and I think they are correct.
#include <iostream>
namespace X {
void p() {
void q(); // This is a block scope declaration of the function q() with external
// linkage (by §3.5/6), which then must be defined in namespace X,
// according to §3.5/7, and not in the global namespace.
q();
}
}
void q() { std::cout << "q()" << '\n'; }
int main()
{
X::p();
}
Your example is almost identical to the one in [basic.link]/7 - Yes, your interpretation is correct.
Using an undefined function q renders your program ill-formed NDR. Hence VC++ is technically conforming. However, you definitely want to report it.
Note how VC++ produces the same output ("q()") even if we add an inner definition of q:
namespace X {
void p() {
void q();
q();
}
void q() { std::cout << "This would be right"; }
}
void q() { std::cout << "q()" << '\n'; }
…but does have sensible behavior when extern is used.