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
Related
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.
Run the the following C++ program twice. Once with the given destructor and once with std::fesetround(value); removed from the destructor. Why do I receive different outputs? Shouldn't destructor be called after function add? I ran both versions on http://cpp.sh/ and Clang++ 6.0, and g++ 7.2.0. For g++, I also included #pragma STDC FENV_ACCESS on in the source code, nothing changed.
#include <iostream>
#include <string>
#include <cfenv>
struct raii_feround {
raii_feround() : value(std::fegetround()) { }
~raii_feround() { std::fesetround(value); }
inline void round_up () const noexcept { std::fesetround(FE_UPWARD ); }
inline void round_down() const noexcept { std::fesetround(FE_DOWNWARD); }
template<typename T>
T add(T fst, T snd) const noexcept { return fst + snd; }
private:
int value; };
float a = 1.1;
float b = 1.2;
float c = 0;
float d = 0;
int main() {
{
raii_feround raii;
raii.round_up();
c = raii.add(a, b);
}
{
raii_feround raii;
raii.round_down();
d = raii.add(a, b);
}
std::cout << c << "\n"; // Output is: 2.3
std::cout << d << "\n"; // Output is: 2.3 or 2.29999
}
Using the floating-point environment facilities requires inserting #pragma STDC FENV_ACCESS on into the source (or ensure that they default to on for the implementation you are using. (Although STDC is a C feature, the C++ standard says that these facilities are imported into C++ by the <cfenv> header.)
Doing so at cpp.sh results in “warning: ignoring #pragma STDC FENV_ACCESS [-Wunknown-pragmas]”.
Therefore, accessing and modifying the floating-point environment is not supported by the compiler at cpp.sh.
All I needed to do was to do std::cout << std::setprecision(30); before calling std::cout in the code (iomanip should be included as well).
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.
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)
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.