I've run into some trouble trying to clean up g++ compiler warnings.
Say I have this class:
class A
{
public:
[[noreturn]] virtual void will_throw() { throw 0; }
};
And inside a non-void function I invoke will_throw without returning.
If I do this by value, i.e.:
int g()
{
A a;
a.will_throw();
}
then I get no -Wreturn-type warnings.
If I do it by pointer:
int g()
{
A a;
A* aptr = &a;
aptr->will_throw();
}
Then I get "warning: no return statement in function returning non-void [-Wreturn-type]"
If I remove virtual from the declaration of A::will_throw then calling it on a pointer also produces no warnings. Calling the method on a reference seems to produce a warning if the method is pure-virtual, but not otherwise.
I wasn't able to find anything saying this is how it's supposed to work, and none of these cases produce warnings in Clang. Is this a bug in GCC?
Since the function is virtual, the compiler doesn’t know (without tracking assignments) that the call through a pointer (or reference) is to A::will_throw and not to some overriding function that might not be noreturn. Since it’s just a warning, both behaviors (or never warning, or always warning!) are conforming.
Related
I found one more case when compiler thinks that static_cast may return nullptr on non-nullptr argument. It seems that this time there is no undefined behavior, like was before, but maybe I'm missing something.
Here's the code compiled with gcc with -O1 -fPIC -Wnull-dereference -Werror options:
struct IR {
virtual ~IR() = default;
};
struct IF {
virtual ~IF() = default;
virtual void get() {};
};
struct SC : IR, IF {
bool h(); // { return true; };
};
struct HD : public IR {
virtual void g() {
if (r_ && r_->h()) {
static_cast<IF*>(r_)->get();
}
}
SC* r_ = nullptr;
};
bool SC::h() { return true; }
HD hd;
Any one of the following conditions will eliminate potential null pointer dereference warning:
making SC::h() inline;
making IF::get() non-virtual;
changing optimization level from -O1 to -O0;
removing -fPIC option...
So, is there a GCC bug, or still UB in code, or static_cast for non-nullptr can results in nullptr?
Link to godbolt.
If you use -fPIC and don't use an inline function for h, then the compiler can't make any assumptions about the behavior of h because GCC's default semantics are to allow any shared library to override the function with alternative semantics. You can additionally specify -fno-semantic-interposition to allow GCC to optimize under the assumption that other implementations of the function loaded from a shared library will not behave differently.
As far as I understand -Wnull-dereference isn't guaranteed to only warn if there is a definitive null pointer dereference, only if there is a potential one (although documentation seems a bit unclear, see also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86172). The compiler can't be sure that r_->h() doesn't modify r_ for the reason above, and therefore the null pointer check cannot prove that static_cast<IF*>(r_)->get(); won't dereference a null pointer.
The warning doesn't appear for -O0 because it is documented that the warning only works when -fdelete-null-pointer-checks is enabled, which it is only with optimizations enabled.
I guess the warning doesn't appear if get isn't virtual either intentionally because the compiler can be sure that the function call won't actually cause a null dereference in the practical sense even if the object pointer is a null pointer (although it is still UB per standard), or alternatively the function is inlined earlier than the null pointer check is performed. (If the function is virtual the compiler can again make no assumptions about this call, since it could be calling a derived class override instead.)
I want to understand how following program works.
class A{
public:
virtual void fun();
};
void A:: fun()
{
cout << "fun() called";
}
int main() {
A *ptr_a;
ptr_a->fun();
return 0;
}
It doesn't prints "fun() called". I tried to find a reasonable explanation for this but I couldn't. If I remove virtual from the declaration then it works fine. Also there is no runtime error.
There is no reason to expect this to work, and no reason to expect it not to work.
You're calling a function on an object that doesn't exist, through an uninitialised and invalid pointer.
Anything can happen.
Anything.
In practice you're not going to see great virtual dispatch results since these rely on data stored within the object that doesn't exist, whereas a normal function call is simpler and can "just happen" without needing to really dereference the pointer.
But that's besides the point really, because compilers are complicated and all intended meaning (whatever that is, in a program with undefined behaviour!) can easily be lost from your code in the process of translation and optimisation.
A language that cares a little less about "pay for what I don't use" could make this a hard error, but that's not how C++ works.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
When does invoking a member function on a null instance result in undefined behavior?
The person in this question failed to create a pointer to an object using 'new', an then used this wild pointer to call a member function on the object.
The program fails when a member-variable is accessed (at myPoint[i].x = xData;).
Why does it fail at that location and not earlier?
The line in question is the first position where this is referenced.
And why not? Standard say that this program has undefined behavior, but in C++ we have 3 kind of functions in a class:
// 1) static functions
class test {
public:
static void foo( int n );
};
In this example foo is like a global function with same signature, but C++ have a different rule for access check and name of it.
// 2) normal member functions
class test {
public:
void foo( int n );
};
In almost all compilers this function is same as a a free function with signature void foo(foo* this, int n) and this is not an error to call this function with an invalid this unless you access this in the function that in that case function possibly generate segmentation fault or even worse, change some unexpected point of your memory.
// 3) virtual functions
class test {
public:
virtual void foo( int n );
};
In this case class test contain an extra invisible member that usually called vtable and contain one pointer to implementation of each virtual function of the class and using this technique C++ can have late binding at runtime, so in this case when you call foo compiler write something like
this->vtable[foo_index]( this, n );
so in this case invalid this immediately show itself and possibly generate errors like segmentation fault or invalid instruction, since you access this at point of call. So as you see in almost all implementation you can have a member function that called through an invalid this pointer and never generate any error(if you never access this in the function), but it can't be virtual.
But remember what explained above completely depend on your compiler, but almost all compilers implement it like above.
You should read more about Undefined Behavior and to build on the example:
struct A { A(int i): _i(i) {} void print() const { printf("%d", _i); } int _i; };
void function(A* a) {
if (a == 0) { printf("%s", "A is null!"); }
a->print();
}
What do you expect for function(0); ?
Well, you seem to expect that A is null! will get printed, and then the program will crash, right ? Maybe... maybe not.
The compiler might actually proceed like so:
a->print();: a cannot be null here, otherwise this statement is meaningless
a == 0 is therefore always false
the function can be rewritten as void function(A* a) { a->print(); }
And then, on a common Linux platform, you will get your crash (SEGFAULT), but no message... at all!
That's what undefined behavior does with optimizations: your program runs a lot faster, but dies in unfathomable ways at the slightest mistakes.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Calling class method through NULL class pointer
I was asked this question in the interview can someone answer it?
#include<string>
#include<iostream>
#include <stdio.h>
using namespace std;
class A
{
int k;
public:
void f1()
{
int i;
printf("1");
}
void f2()
{
k = 3;
printf("3");
}
};
class B
{
int i;
public:
virtual void f1()
{
printf("2");
scanf("%d",&i);
}
};
int main()
{
A* a = NULL;
B* b = NULL;
a->f1(); // works why?(non polymorphic)
b->f1(); // fails why?(polymorphic)
a->f2(); //fails why?
}
The last 2 cases are of polymorphic classes. The first case is a normal class .i understand that if i access i in f1 of A it will again give a runtime exception . but i am not getting why that happens
I agree with the other posts that this is undefined behavior, meaning anything can happen when executing the program, including "doing the right thing".
Now, let's look at how the calls are implemented:
a->f1() is a normal method call (non virtual). Most compilers will compile this in a similar way as the following code:
class A { int i; }
void f1(A* a) { int i; printf("1"); }
Meaning the this pointer is actually handled like a parameter to the function (in practice there are frequently some optimizations about how the this pointer is handled, but that is irrelevant here). Now, since f1 doesn't use the this pointer, the fact that it is null doesnt cause a crash.
a->f2() will actually crash because it uses the this pointer: it updates this->k.
The call to b->f1() is a virtual function call, and this is typically implemented using a virtual table lookup as b->vtable[0](). Since b is null, the dereference to read the virtual table crashes.
a->f1();
b->f1();
a->f2();
In all the three cases you are deferefencing a pointer that points to NULL, i.e. it does not point to an object. This constitutes undefined behaviour. They may work by pure chance, but you cannot rely on that. It also doesn't makes much sense to try to figure out why one version could possibly work. Undefined behaviour means anything can happen.
Technically this is all undefined behaviour. So without more background (compiler, used settings) this would be the correct answer.
I don't believe this is what they expected to hear though.
Given that usually a member function call is internally translated in this manner (simplified on purpose):
class A {
void foo(int x) {} // compiler creates function void A_foo(A* this, int x) {}
};
A a;
a.foo(5); // compiler calls A_foo(&a, 5);
BUT the situation is different for virtual functions. I will not explain the principle of virtual dispatch here, but to simplify - the function that gets called in the end is dependent on the dynamic type of the object. If the object doesn't exist, the program can't know what function to call.
As to why your a->f2() fails. Imagine the function A_f2(A* this). Inside you access A's member k. This would in my simplified compiler get translated to this->k = 3. But in the actual call this is a null pointer.
In a way neither of the three cases "works". But in another way, all of the three cases "work".
They all have undefined behaviour, because they all perform indirection through a null pointer.
i understand that if i access i in f1 of A it will again give a runtime exception
Maybe, maybe not. Undefined behaviour is undefined, so anything can happen.
All three examples result in undefined behavior, so this is very implementation specific and not guaranteed to have the same behavior on all compilers.
A common way of implementing virtual functions is to add a pointer to a table of function pointers at the beginning of a class. Whenever a virtual function is called, the program follows this pointer and looks in the table to determine which function to call. Since, in the example of a null-pointer, it is looking at an invalid address for this pointer, this causes a runtime error.
When calling a non-virtual function, the compiler already knows exactly what function to call, so it can directly insert a call to this function; accessing the object is not necessary to determine which function to call. So, if the function itself does not access the object, the function call will never result in an access through the null-pointer, so it won't result in a runtime error.
I just wanted to know whether is this following block of code fully valid in C++:
class A
{
public:
virtual bool b() = 0;
};
class B
{
public:
virtual bool b() = 0;
};
class C: public A, public B
{
public:
virtual bool A::b()
{
return true;
}
virtual bool B::b()
{
return false;
}
};
Using VS2008 it compiles without any errors, however, on GCC (MinGW) 3.4.5 it gives me errors like:
cannot declare member function `A::b' within `C'
On the lines where the virtual methods are implemented. I was curious if this is just generally considered invalid and by C++ standards forbidden code (and in VS it thus works thanks to some MS non-standardized magic), or only a bug or unsupported language feature in GCC.
No it's not valid. You can't override them separately like that, because they would have the same signature.
There is a guru of the week about that.
The qualified name A::b is not allowed as the name of a member of class C.
It should not compile due to parents having the same function name. Furthermore, you aren't suppose to overload the function b more than once.
If you refer to the virtual table created for this:
0(beginning of vtable) - A::b() -> B::b()
You see, since class B has the same function name as class A, it overrides it, thus you have now B::b() to override (since it's still pure). This is due to multiple inheritancy. How would the compiler be able to differenciate between the two (they have the same signature)? Generally, this will fail, because like I just said, the compiler isn't suppose to make decisions, it's supposed to tell you that there is a problem.
It compiles on VS, but have you tried running it (included in a file where it is actually created)? Sometimes, compiler are lazy and don't pop errors on classes that aren't used.
Just as an FYI, VC gives the error only when you try to use the b method:
C:\temp\test.cpp(33) : error C2668: 'C::b' : ambiguous call to overloaded function
C:\temp\test.cpp(23): could be 'bool C::b(void)'
C:\temp\test.cpp(18): or 'bool C::b(void)'
while trying to match the argument list '(void)'
And for what it's worth, Comeau's compiler behaves similarly, but with an even more confusing error message:
"C:\temp\test.cpp", line 33: error: no instance of overloaded function "C::b"
matches the argument list
object type is: C
bool z = c.b();