Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Comparing this Pointer to Null

102 views
Skip to first unread message

Christiano

unread,
Jul 4, 2017, 6:11:38 PM7/4/17
to
The book PPP2[1] presents the following code, Page 620 (2nd edition, fourth print):

Link* Link::insert(Link* n) // insert n before this object; return n
{
if (n==nullptr) return this;
if (this==nullptr) return n;
n–>succ = this; // this object comes after n
if (prev) prev–>succ = n;
n–>prev = prev; // this object’s predecessor becomes n’s predecessor
prev = n; // n becomes this object’s predecessor
return n;
}

What interested me in this code was exactly this part:

if (this==nullptr) return n;

So I searched about "null comparing this" using search engine.

This website..
https://www.viva64.com/en/b/0226/
...says:
"First of all according to the C++ standard (it follows from the paragraph 5.2.5/3 of the standard ISO/IEC 14882:2003(E)), calling any nonstatic
method of any class through a null-pointer leads to undefined behavior. "

But the ISO/IEC 14882:2003, § 5.2.5.3 says:
If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form
(*(E1)).E2; the remainder of 5.2.5 will address only the first option (dot) [59] . Abbreviating object-
expression.id-expression as E1.E2, then the type and lvalue properties of this expression are determined as
follows. In the remainder of 5.2.5, cq represents either const or the absence of const; vq represents
either volatile or the absence of volatile. cv represents an arbitrary set of cv-qualifiers, as defined
in 3.9.3.
[59] Note that if E1 has the type “pointer to class X”, then (*(E1)) is an lvalue.

That is, the standard does not say exactly "calling any nonstatic method of any class through a null-pointer leads to undefined behavior".
And (*(E1)) being a lvalue when E1 is null is not a problem IF THE PROGRAMMER IS AWARE OF WHAT HE IS DOING.

What I'm trying to say is exactly:

The "Comparing 'this' Pointer to Null as an undefined behavior" is a MYTH which is not based on Standard.

You can see this myth in several websites:
https://www.google.com/#q=this+null+c%2B%2B

Following this reasoning, Stroustrup is correct, g++ is correct [2], and Clang is correct [3].
And the standard shouldn't forbid it because it has been used correctly for a long time without problems so that it would be much fairer to be
interpreted as a technique.

What do you think about it? Please comment if you disagree or agree and the reason.

[1] http://stroustrup.com/Programming/

[2] testing test.cpp (code bellow) using g++
$ g++ test.cpp -std=c++11
$ ./a.out
Test
$

[3] testing test.cpp (code bellow) using clang
$ CC test.cpp -std=c++11
$ ./a.out
Test
$

///////// test.cpp: /////////////

#include <iostream>

using namespace std;

struct X
{
void f()
{
cout << "Test" << endl;

}
};

int main()
{
X *x = nullptr;

x->f();

return 0;
}
/////////----------- eof

Alf P. Steinbach

unread,
Jul 4, 2017, 7:40:43 PM7/4/17
to
On 05-Jul-17 12:11 AM, Christiano wrote:
> The book PPP2[1] presents the following code, Page 620 (2nd edition,
> fourth print):
>
> Link* Link::insert(Link* n) // insert n before this object; return n
> {
> if (n==nullptr) return this;
> if (this==nullptr) return n;

Looks like a typo.

It could be a thinko from habit, though, because when Bjarne designed
the language it didn't have exceptions, so the way to signal
construction failure was to do `this = 0;`. So at that time, before
exceptions, the `this` pointer could certainly be 0.

As I recall we got exception around 1989-1991, the time of the ARM.


> n–>succ = this; // this object comes after n
> if (prev) prev–>succ = n;
> n–>prev = prev; // this object’s predecessor
> becomes n’s predecessor
> prev = n; // n becomes this object’s
> predecessor
> return n;
> }
>
> What interested me in this code was exactly this part:
>
> if (this==nullptr) return n;
>
> So I searched about "null comparing this" using search engine.
>
> This website..
> https://www.viva64.com/en/b/0226/
> ...says:
> "First of all according to the C++ standard (it follows from the
> paragraph 5.2.5/3 of the standard ISO/IEC 14882:2003(E)), calling any
> nonstatic method of any class through a null-pointer leads to undefined
> behavior. "

Right.


> But the ISO/IEC 14882:2003, § 5.2.5.3 says: If E1 has the type
> “pointer to class X,” then the expression E1->E2 is converted to the
> equivalent form (*(E1)).E2;

There is an infamous note (notes are non-normative in ISO standards) in
C++14 §8.3.2/5 (or C++98 §8.3.2/4): “in particular, a null reference
cannot exist in a well-defined program, because the only way to create
such a reference would be to bind it to the “object” obtained by
indirection through a null pointer, which causes undefined behavior”

It's infamous because it /claims/ that the standard elsewhere prescribes
UB for *p when p is a null pointer, but there is no such wording.

Still, everyone agrees that it's UB to dereference a nullpointer, except
in a `typeid` expression, where you're guaranteed an exception in that case.


> the remainder of 5.2.5 will address only
> the first option (dot) [59] . Abbreviating object-
> expression.id-expression as E1.E2, then the type and lvalue
> properties of this expression are determined as follows. In the
> remainder of 5.2.5, cq represents either const or the absence of
> const; vq represents either volatile or the absence of volatile. cv
> represents an arbitrary set of cv-qualifiers, as defined in 3.9.3.
> [59] Note that if E1 has the type “pointer to class X”, then (*(E1))
> is an lvalue.
>
> That is, the standard does not say exactly "calling any nonstatic method
> of any class through a null-pointer leads to undefined behavior".
> And (*(E1)) being a lvalue when E1 is null is not a problem IF THE
> PROGRAMMER IS AWARE OF WHAT HE IS DOING.

The compiler is free to insert code that checks for nullpointer and if
so, plants some nasty NSA malware on your computer.


> What I'm trying to say is exactly:
>
> The "Comparing 'this' Pointer to Null as an undefined
> behavior" is a MYTH which is not based on Standard.

No, not a myth, but the standard has never been fixed. It /claims/, in
non-normative text, that it says, but it doesn't actually say. That this
state of affairs has lasted through three and now soon four revisions is
pretty weird, but presumably more important stuff's been fixed instead.


> You can see this myth in several websites:
> https://www.google.com/#q=this+null+c%2B%2B
>
> Following this reasoning, Stroustrup is correct,

No, that code is just a typo or thinko.


> g++ is correct [2], and Clang is correct [3].

Yes, UB means that they can do whatever. So I haven't even checked what
you cite that they do. Or don't.


> And the standard shouldn't forbid it because it has been used correctly
> for a long time without problems so that it would be much fairer to be
> interpreted as a technique.

It was used for a long time – before C++ got exceptions.

Which was roughly 10 years before the first standardization.


[snip]

Cheers & hth.,

- Alf

Christiano

unread,
Jul 4, 2017, 9:24:46 PM7/4/17
to
On 07/04/17 20:40, Alf P. Steinbach wrote:
> On 05-Jul-17 12:11 AM, Christiano wrote:
>> The book PPP2[1] presents the following code, Page 620 (2nd edition, fourth print):
>>
>> Link* Link::insert(Link* n) // insert n before this object; return n
>> {
>> if (n==nullptr) return this;
>> if (this==nullptr) return n;
>
> Looks like a typo.
>
> It could be a thinko from habit, though, because when Bjarne designed the language it didn't have exceptions, so the way to signal construction
> failure was to do `this = 0;`. So at that time, before exceptions, the `this` pointer could certainly be 0.

It was not that, because after he says:

-------- excerpt begin ---------
Note that this has a specific meaning: it points to the object for which a member function is called. It does not point to any old object. The
compiler ensures that we do not change the value of this in a member function. For example:

struct S {
// . . .
void mutate(S* p)
{
this = p; // error: this is immutable
// . . .
}
};
------------excerpt end ----------

The Link * that the function returns is the first link from a doubly linked-list (with Gods[1] inside).
And if, for example:
Link *p = nullptr; // EMPTY LIST
p = p.insert(new Link{"Athena"}); // INSERTING ATHENA

It will work just because insert return the new Athena making it as the new element of the list.

I'm sure it was not a mistake.

> As I recall we got exception around 1989-1991, the time of the ARM.
>
>
>> n–>succ = this; // this object comes after n
>> if (prev) prev–>succ = n;
>> n–>prev = prev; // this object’s predecessor becomes n’s predecessor
>> prev = n; // n becomes this object’s predecessor
>> return n;
>> }
>>
>> What interested me in this code was exactly this part:
>>
>> if (this==nullptr) return n;
>>
>> So I searched about "null comparing this" using search engine.
>>
>> This website..
>> https://www.viva64.com/en/b/0226/
>> ...says:
>> "First of all according to the C++ standard (it follows from the paragraph 5.2.5/3 of the standard ISO/IEC 14882:2003(E)), calling any nonstatic
>> method of any class through a null-pointer leads to undefined behavior. "
>
> Right.
>
>
>> But the ISO/IEC 14882:2003, § 5.2.5.3 says: If E1 has the type
>> “pointer to class X,” then the expression E1->E2 is converted to the
>> equivalent form (*(E1)).E2;
>
> There is an infamous note (notes are non-normative in ISO standards) in C++14 §8.3.2/5 (or C++98 §8.3.2/4): “in particular, a null reference cannot
> exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by indirection through a
> null pointer, which causes undefined behavior”
>> It's infamous because it /claims/ that the standard elsewhere prescribes UB for *p when p is a null pointer, but there is no such wording.

Exactly! There is no such wording. The difference here is:

✓ You and several peoples are saying that there should be such wording.
✓ I am saying that there should NOT be such wording.

And this note should be corrected because it doesn't consider situations where the dereference doesn't access data and so shouldn't cause UB.
The error here is to consider that all dereference implies data access.

Obviously, if you do:
int *a = nullptr;
*a = 10;

Of course, it is UB because the code above is obviously wrong. But the Stroustrup code, very interesting in my opinion, makes perfect sense.

A specification fails to encompass all possibilities at first, the use of language showed that in this particular topic it has failed to foresee some
possibilities, it is normal. So, in my opinion, it makes more sense to adapt the specification to reality than the reality to adapt to a possible and
vague interpretation of the specification, which would invalid very interesting codes like that stroustrup's code.

[1] Please, I am not promoting or disrespecting any religion, I am just following the example of the book which contain lists of ancient Greek and
Nordic gods.

Richard Damon

unread,
Jul 4, 2017, 10:34:08 PM7/4/17
to
The problem is that the dereferencing of a null pointer is undefined
behavior, so you haven't escaped the problem. Yes, sometimes if you know
the implementation well enough, and are willing to take your chances it
won't change, sometimes you can get away with undefined behavior. That
doesn't make it reasonable to say the behavior shouldn't be undefined.

Part of the issue with this is that the implementation can (and
sometimes must) access hidden pieces of the object (like what is
typically called the vtable) to work with an object, and the function
might do so in its preamble as part of its standard operation (even if
it later might see it doesn't need to). AFAIK, the only object that
can't have this sort of data stored in them is what is called a POD
(Plain Ole Data) which are layout compatible with C code.

My memory is that early in the development of C++ there were cases where
you could end up with a null this, but well before it came to the first
standard this was dropped,

Marcel Mueller

unread,
Jul 5, 2017, 1:07:07 AM7/5/17
to
On 05.07.17 03.24, Christiano wrote:
> The Link * that the function returns is the first link from a doubly
> linked-list (with Gods[1] inside).
> And if, for example:
> Link *p = nullptr; // EMPTY LIST
> p = p.insert(new Link{"Athena"}); // INSERTING ATHENA
>
> It will work just because insert return the new Athena making it as the
> new element of the list.

Invoking insert() on an object of type Link* won't compile.

p = p->insert(new Link{"Athena"}); // INSERTING ATHENA
might compile. But it is UB.

The latter might also work es expected. UB always included the case that
it works as expected. It is just not reliable.


> Exactly! There is no such wording. The difference here is:
>
> ✓ You and several peoples are saying that there should be such wording.
> ✓ I am saying that there should NOT be such wording.
>
> And this note should be corrected because it doesn't consider situations
> where the dereference doesn't access data and so shouldn't cause UB.

This constraint is not sufficient.
E.g. if in the above example insert is virtual it will crash. Even when
the invoked method does not access any data.
So it will basically only be valid for POD structures. But this prevents
the constructor of Link to exist.

> The error here is to consider that all dereference implies data access.
>
> Obviously, if you do:
> int *a = nullptr;
> *a = 10;
>
> Of course, it is UB because the code above is obviously wrong. But the
> Stroustrup code, very interesting in my opinion, makes perfect sense.

I have also written code which allows to invoke some simple functions on
a null object in very rare cases. But this code was never intended to be
portable.
A specific platform might always give further guarantees, shrinking the
area of UB.

> A specification fails to encompass all possibilities at first, the use
> of language showed that in this particular topic it has failed to
> foresee some possibilities, it is normal. So, in my opinion, it makes
> more sense to adapt the specification to reality than the reality to
> adapt to a possible and vague interpretation of the specification, which
> would invalid very interesting codes like that stroustrup's code.

Feel free to submit a draft.


Marcel

Christiano

unread,
Jul 5, 2017, 2:23:50 AM7/5/17
to
On 07/05/17 02:06, Marcel Mueller wrote:
> On 05.07.17 03.24, Christiano wrote:
>
> Invoking insert() on an object of type Link* won't compile.

Correcting:
Link *p = nullptr; // EMPTY LIST
p = p->insert(new Link{"Athena"}); // INSERTING ATHENA
// p is pointing to Athena now.
// p-->[athena]
p = p->insert(new Link{"Odin"}); // INSERTING Odin
// p is pointing to Odin now.
// p-->[odin]<->[athena]


> This constraint is not sufficient.
> E.g. if in the above example insert is virtual it will crash. Even when the invoked method does not access any data.

Yes, it will crash because virtual methods necessarily need to access data (virtual table) which compose the class object. But this case would be a
wrong code. What is wrong with Stroustrup code? Nothing, because nonvirtual methods doesn't influence the object bytes. The Stroustrup's code is very
interesting. Why it should be UB when it is not? You are talking about a different case, the Insert is not virtual therefore
p->insert(new Link{"Athena"});
will not access any data from nullptr position (Insert will return on 2nd line).

> So it will basically only be valid for POD structures. But this prevents the constructor of Link to exist.

The stroustrup code works. The Link is defined as:

class Link {
public:
string value;

Link(const string& v, Link* p = nullptr, Link* s = nullptr)
: value{v}, prev{p}, succ{s} { }

Link* insert(Link* n) ; // insert n before this object
Link* add(Link* n) ; // insert n after this object
Link* erase() ; // remove this object from list
Link* find(const string& s); // find s in list
const Link* find(const string& s) const; // find s in const list (see §18.5.1)

Link* advance(int n) const; // move n positions in list

Link* next() const { return succ; }
Link* previous() const { return prev; }
private:
Link* prev;
Link* succ;
};

It is not a POD.


In C programming, it is common the following code:

struct X *a = NULL;
// ...
a = (struct X *) malloc(N*sizeof(*a)); // EQUIVALENT: a = (struct X *) malloc(N*sizeof(struct X));

See: sizeof(*a)
Does *a here cause UB? It will not cause UB... because sizeof(*a) actually doesn't need to access *a at all (it is a static operator).

Now, see again:

Link *p = nullptr; // EMPTY LIST
p = p->insert(new Link{"Athena"}); // INSERTING ATHENA

equivalent:
p = (*p).insert(new Link{"Athena"});
The same thing: when compiler is generating code, this line actually doesn't need to generate code that access *p at all, p can be anything, In any
architecture: MIPS, ARM, x86, Risc-V, SPARC, POWER, Microcontrollers whatever. In any Operating system: windows, macosx, linux, BSD, RTOS's whatever.

Therefore comparing "this" with "nullptr" should be recognized as a technique.

Mr Flibble

unread,
Jul 5, 2017, 12:13:17 PM7/5/17
to
On 05/07/2017 07:23, Christiano wrote:
> Therefore comparing "this" with "nullptr" should be recognized as a
> technique.

Wrong. Dereferencing a null pointer is UB so 'this' should never by null
unless you are invoking UB.

/Flibble

peter koch

unread,
Jul 5, 2017, 1:59:28 PM7/5/17
to
Den onsdag den 5. juli 2017 kl. 00.11.38 UTC+2 skrev Christiano:
> The book PPP2[1] presents the following code, Page 620 (2nd edition, fourth print):
>
> Link* Link::insert(Link* n) // insert n before this object; return n
> {
> if (n==nullptr) return this;
> if (this==nullptr) return n;
> n–>succ = this; // this object comes after n
> if (prev) prev–>succ = n;
> n–>prev = prev; // this object’s predecessor becomes

A conforming C++ compiler will know that this never can be null and will replace if (this == nullptr) with if (true). In fact clang gives this warning:
<source>:6:13: warning: 'this' pointer cannot be null in well-defined C++ code; comparison may be assumed to always evaluate to true [-Wtautological-undefined-compare]

Marcel Mueller

unread,
Jul 5, 2017, 2:19:41 PM7/5/17
to
On 05.07.17 08.23, Christiano wrote:
>> This constraint is not sufficient.
>> E.g. if in the above example insert is virtual it will crash. Even
>> when the invoked method does not access any data.
>
> Yes, it will crash because virtual methods necessarily need to access
> data (virtual table) which compose the class object.

The existence of the vtable pointer is already implementation defined.
Although this is quite common.

> But this case would
> be a wrong code. What is wrong with Stroustrup code? Nothing, because
> nonvirtual methods doesn't influence the object bytes. The Stroustrup's
> code is very interesting. Why it should be UB when it is not? You are
> talking about a different case, the Insert is not virtual therefore
> p->insert(new Link{"Athena"});
> will not access any data from nullptr position (Insert will return on
> 2nd line).

Well, the standard simply does not require this kind of restriction for
implementers. Implementations /might/ access the object behind *this of
any non POD type for any implementation specific purpose. Not the
functions are the problem, but the existence of a constructor makes the
type non POD.

And AFAIK even for POD types this == nullptr is not well defined
behavior. Although it is likely to work since POD types should not
contain any additional internal data members.

On the other hand the code will also crash if inheritance comes into
play. In case of multiple inheritance the raw pointer value has to be
modified at explicit or implicit type conversions. This implies a
nullptr-check unless the compiler is sure that the raw pointer cannot be
nullptr. In fact the generated code for references and *this usually
just adds a constant while for pointer types a conditional expression is
generated. So in this case nullptr might be converted to some nonzero
value and the this == nullptr check will fail. Again UB.

>> So it will basically only be valid for POD structures. But this
>> prevents the constructor of Link to exist.
>
> The stroustrup code works.

You can't tell this. It works on some platforms, maybe on many. But to
tell that it always works (with a C++ compatible compiler) it must work
on any platform, including the ones that do not yet exist. You can't
prove that unless the code is standard conformant.


> The Link is defined as:
>
[...]
> It is not a POD.

Indeed.


> In C programming, it is common the following code:
>
> struct X *a = NULL;
> // ...
> a = (struct X *) malloc(N*sizeof(*a)); // EQUIVALENT: a = (struct X *)
> malloc(N*sizeof(struct X));
>
> See: sizeof(*a)
> Does *a here cause UB? It will not cause UB... because sizeof(*a)
> actually doesn't need to access *a at all (it is a static operator).

Exactly. Neither sizeof nor typeof evaluate the expression.

> equivalent:
> p = (*p).insert(new Link{"Athena"});
> The same thing: when compiler is generating code, this line actually
> doesn't need to generate code that access *p at all, p can be anything,

The invocation of insert() /might/ access *p.


> In any architecture: MIPS, ARM, x86, Risc-V, SPARC, POWER,
> Microcontrollers whatever. In any Operating system: windows, macosx,
> linux, BSD, RTOS's whatever.

Well, making assumptions on platforms based on statistics is risky.
E.g., I have worked on a platform where *(int*)0 might access a valid
memory address (inmos T80x).

> Therefore comparing "this" with "nullptr" should be recognized as a
> technique.

The standard might be enhanced in the way that this is well defined
behavior for POD types. For now it is not.
But I am in doubt that this will ever be defined for non POD types like
the Link class above.


Marcel

Manfred

unread,
Jul 6, 2017, 10:31:00 AM7/6/17
to
On 7/5/2017 8:23 AM, Christiano wrote:
>
> The stroustrup code works.
It works in many (most, all?) implementations that are available today,
but still IMHO there is a flaw of robustness with respect to formal
requirements of the standard:
The problem is before the test (this == nullptr); in:
T* p = nullptr;
f->foo();

here -> is /formally/ dereferencing the pointer, even if implementations
may not access the referenced memory location until there is a /data/
access (as opposed to member function call).
I think it is a pity that the standard is not more explicit about the
formal specification of pointer dereference for member function calls.
This may lead to problems like the one that Peter Koch has reported
about clang.

It may be interesting to note that the technique is quite common at
least in one implementation - notably MFC: here the test if(this ==
NULL) is very common (e.g. GetSafeHwnd()), /but/ here it is essential
that MFC is delivered together with VC++, so Microsoft is in full
control of the compiler as well, i.e. /how/ this code is compiled.


The Link is defined as:
>
[...]
> Link* insert(Link* n) ; // insert n before this object
>
> It is not a POD.
>
>
> In C programming, it is common the following code:
>
The reference to C is interesting in another sense:
A C translation of the insert() method above would be:

struct Link* insert(struct Link* thisLink, struct Link* n);

Here thisLink (which would play the role of the this pointer above) is
/explicitly/ an argument to insert(), so the C standard would give full
guarantee of how and when it would be dereferenced, and:

struct Link* p = NULL;
p = insert(p, n); // n is a struct Link*

would be perfectly legal.
This is the main difference the C and C++ implementation with respect to
a null this pointer.
By the way, recalling the above about MFC, the VC++ documentation
describes the 'this' pointer as a 'hidden argument' to nonstatic member
functions.
I think that it is such a description of 'this' as a hidden argument
that Bjarne had in mind when he wrote the OP code, but, the way I
understand it, the standard uses different wording.

>
> Now, see again:
>
> Link *p = nullptr; // EMPTY LIST
> p = p->insert(new Link{"Athena"}); // INSERTING ATHENA
>
> equivalent:
> p = (*p).insert(new Link{"Athena"});
> The same thing: when compiler is generating code, this line actually
> doesn't need to generate code that access *p at all. [...]
This is not clearly guaranteed by the standard (I would love to be
proven wrong), and the fact that an implementation does not /need/ to
access *p does not mean that an implementation is /forbidden/ to access *p.

> Therefore comparing "this" with "nullptr" should be recognized as a
> technique.
Not with the current standard, I think.

Manfred

unread,
Jul 6, 2017, 11:01:47 AM7/6/17
to
On 7/6/2017 4:30 PM, Manfred wrote:
> On 7/5/2017 8:23 AM, Christiano wrote:
>> In C programming, it is common the following code:
>>
> The reference to C is interesting in another sense:
> A C translation of the insert() method above would be:
>
> struct Link* insert(struct Link* thisLink, struct Link* n);
>
> Here thisLink (which would play the role of the this pointer above) is
> /explicitly/ an argument to insert(), so the C standard would give full
> guarantee of how and when it would be dereferenced, and:
>
> struct Link* p = NULL;
> p = insert(p, n); // n is a struct Link*
>
> would be perfectly legal.
> This is the main difference the C and C++ implementation with respect to
> a null this pointer.
> By the way, recalling the above about MFC, the VC++ documentation
> describes the 'this' pointer as a 'hidden argument' to nonstatic member
> functions.
> I think that it is such a description of 'this' as a hidden argument
> that Bjarne had in mind when he wrote the OP code, but, the way I
> understand it, the standard uses different wording.

In fact the standard says (n4618 9.2.2.1): "the keyword this is a
prvalue expression whose value is the address of the object for which
the function is called"
Which would imply that an object /is/ present at 'this' address, which
in turn rules out nullptr.

peter koch

unread,
Jul 6, 2017, 11:28:44 AM7/6/17
to
Den torsdag den 6. juli 2017 kl. 16.31.00 UTC+2 skrev Manfred:
> On 7/5/2017 8:23 AM, Christiano wrote:
> >
> > The stroustrup code works.
> It works in many (most, all?) implementations that are available today,
> but still IMHO there is a flaw of robustness with respect to formal
> requirements of the standard:
> The problem is before the test (this == nullptr); in:
> T* p = nullptr;
> f->foo();
>
It does not "work" in many implementations today. Actually, I do not see it working on any. Just try it out on godbolt.org - remember to compile with optimisations on.
> here -> is /formally/ dereferencing the pointer, even if implementations
> may not access the referenced memory location until there is a /data/
> access (as opposed to member function call).
> I think it is a pity that the standard is not more explicit about the
> formal specification of pointer dereference for member function calls.
> This may lead to problems like the one that Peter Koch has reported
> about clang.

It is not a problem, and I did not intend it to be reported as such. Invoking a member function without a valid object is UB. A compiler has ´the right to assume that UB will never happen and thus ignore code that would provoke it.

>
> It may be interesting to note that the technique is quite common at
> least in one implementation - notably MFC: here the test if(this ==
> NULL) is very common (e.g. GetSafeHwnd()), /but/ here it is essential
> that MFC is delivered together with VC++, so Microsoft is in full
> control of the compiler as well, i.e. /how/ this code is compiled.
>
Yes. Microsoft has the right to do whatever it sees fit in case of UB, including given it the meaning some might assume is the natural one. I for one would expect the code to be ignored and a warning printed - just as clang does.

Manfred

unread,
Jul 6, 2017, 12:52:57 PM7/6/17
to
On 7/6/2017 5:28 PM, peter koch wrote:
> Den torsdag den 6. juli 2017 kl. 16.31.00 UTC+2 skrev Manfred:
[...]
>> This may lead to problems like the one that Peter Koch has reported
>> about clang.
>
> It is not a problem, and I did not intend it to be reported as such. Invoking a member function without a valid object is UB. A compiler has ´the right to assume that UB will never happen and thus ignore code that would provoke it.

Apologies if I misrepresented your remark (which I think was fully
valid). What I meant with 'problems' was not a problem with clang, but a
problem with the code, that, although from an authoritative source,
would not deliver the expected result on a conformant implementation.

peter koch

unread,
Jul 6, 2017, 2:31:59 PM7/6/17
to
No need to apologise. I did not believe that you misunderstood me, but just wanted to clarify. Comparing this to null is an abomination, so there should be no confusion here. ;-)


/Peter


Daniel

unread,
Jul 6, 2017, 4:19:08 PM7/6/17
to
On Tuesday, July 4, 2017 at 10:34:08 PM UTC-4, Richard Damon wrote:

> ... this was dropped

An unfortunate choice of pronoun!

Öö Tiib

unread,
Jul 6, 2017, 8:11:38 PM7/6/17
to
On Wednesday, 5 July 2017 09:23:50 UTC+3, Christiano wrote:
> On 07/05/17 02:06, Marcel Mueller wrote:
> > On 05.07.17 03.24, Christiano wrote:
> >
> > Invoking insert() on an object of type Link* won't compile.
>
> Correcting:
> Link *p = nullptr; // EMPTY LIST
> p = p->insert(new Link{"Athena"}); // INSERTING ATHENA
> // p is pointing to Athena now.
> // p-->[athena]
> p = p->insert(new Link{"Odin"}); // INSERTING Odin
> // p is pointing to Odin now.
> // p-->[odin]<->[athena]
>
>
> > This constraint is not sufficient.
> > E.g. if in the above example insert is virtual it will crash. Even when
> > the invoked method does not access any data.
>
> Yes, it will crash because virtual methods necessarily need to access
> data (virtual table) which compose the class object. But this case would
> be a wrong code. What is wrong with Stroustrup code? Nothing, because
> nonvirtual methods doesn't influence the object bytes. The Stroustrup's
> code is very interesting. Why it should be UB when it is not? You are
> talking about a different case, the Insert is not virtual therefore
> p->insert(new Link{"Athena"});
> will not access any data from nullptr position (Insert will return on
> 2nd line).

The requirements to C++ language (standard) do not describe how the virtual
and non-virtual functions are implemented. Especially it nowhere mentions
"virtual table" nor that it is "data".

Can you give motivating example why would anyone want to call
non-virtual member functions for a bad pointer or nullptr (lets imagine
that it is defined behavior)?

0 new messages