passing std::shared_ptr to a function?

Started by
24 comments, last by ZachHoefler 10 years, 8 months ago

What is this fad of people using 'this->member' all over the place instead of just referring to members the normal way? It makes my teeth itch.

It's easier to use intelisense with.


True, but there are also a few minor side effects.

First is that if the class overrides operator-> it will use that instead. This is a fairly rare thing, but something to consider.

Second is that it has the potential to cause some unnecessary trips to memory. Most optimizers will strip that away, but as this is the "General Programming" forum rather than "For Beginners", we should not be over-reliant on the optimizer. I've worked on several systems over the years (Nintendo DS, PalmOS, etc.) that when encountered would dereference the this pointer and follow it rather than use the information already loaded in registers.


My current studio includes it is in our programming style guide as a restricted practice. I flag it in code reviews as an error when I see it.

As pointed out above, overriding -> has no effect on this.


That is what makes it confusing, and one of the reasons to avoid it.

The arrow operator is not overriden very often, but it is one of those "gotcha" areas of C++. It generally works the way programmers expect it to work, but there is some subtlety in the drill-down behavior when operator overloading is involved.

Contrived example to demonstrate:


#include <iostream>
 
struct Concrete {
    void foo() { std::cout << "Concrete::foo\n"; }
};
struct Proxy {
    Concrete *a;
    void foo() { std::cout << "Proxy::foo\n"; }
    Concrete* operator->() { return a; }
};
struct Proxy2 {
    Proxy *b;
    void foo() { std::cout << "Proxy2::foo\n"; }
    Proxy& operator->() { return *b; }
 
    void RunFoo() {
        foo(); // Calls Proxy2::foo
        this->foo();  // QUIZ #1:  Which foo() is called?
        (*this).operator->()->foo(); // QUIZ #2: Which foo() is called?
    }
};
int main()
{
    Concrete a;
    Proxy b = { &a };
    Proxy2 c = { &b };
    c.RunFoo();
    c->foo(); // QUIZ #3: Which foo() is called?
}


The answers are not surprising at all when you stop to think about them.

The problem is that you need to stop and think about them.

Advertisement
I don't see why you think that foo() is clearer than this->foo(). To me it's the opposite, because this-> restricts the possible places to look for foo().

There are unintuitive parts to the code above, particularly in how proxy2 acts as a pointer to Concrete, instead of a pointer to Proxy, but that is an unrelated problem.

I guess you could say that the distinction between (*this)->foo() and this->foo() may be easily overlooked, but (*this)->foo() is strange looking enough to warrant special attention. Similarly with (*this).operator->()->foo(), except I don't see why you would ever use that instead of (*this)->foo().

I guess you could say that the distinction between (*this)->foo() and this->foo() may be easily overlooked, but (*this)->foo() is strange looking enough to warrant special attention. Similarly with (*this).operator->()->foo(), except I don't see why you would ever use that instead of (*this)->foo().

I don't know, maybe whenever you want to intentionally make something confusing to try to prove a point.

And yet the fact that operator-> is automatically called recursively until it returns an actual pointer type is occasionally used to implement special behavior or convenient helper functions by using wrapper objects that implement it. It's often the easiest way to keep the calling code clean and blissfully ignorant of implementation details. Try implementing a special proxy on top of a basic proxy to some generic object without exploiting operator->.

Basically, the question is: what happened to old-fashioned ways to mark members like 'mMember' or 'member_' that don't have the potential for obscure side effects?

There is no ambiguity for functions either, as a function is either a member, global or called through some other object. Global functions should be in a namespace and "using namespace everything" is often preferring laziness over clear code. So suddenly foo() is obviously a member function, as otherwise it would be something like std::foo() or ::foo().

f@dzhttp://festini.device-zero.de

And yet the fact that operator-> is automatically called recursively until it returns an actual pointer type is occasionally used to implement special behavior or convenient helper functions by using wrapper objects that implement it. It's often the easiest way to keep the calling code clean and blissfully ignorant of implementation details. Try implementing a special proxy on top of a basic proxy to some generic object without exploiting operator->.

Basically, the question is: what happened to old-fashioned ways to mark members like 'mMember' or 'member_' that don't have the potential for obscure side effects?

There is no ambiguity for functions either, as a function is either a member, global or called through some other object. Global functions should be in a namespace and "using namespace everything" is often preferring laziness over clear code. So suddenly foo() is obviously a member function, as otherwise it would be something like std::foo() or ::foo().

What's the side effect? this and *this are totally different objects, and anyone who confuses the two should not be using nested proxies.

I also disagree that global functions should always be namespace qualified, because doing so prevents argument dependent lookup. So for example, you should prefer to call unqualified swap, and a using statement rather than calling std::swap.

The point is not that it causes ambiguity or strange behavior.

The point is that it makes the programmer stop for a moment to figure it out.

Code should be obvious. You should generally be able to tell at a glance what the code does. Code that does something tricky should include a great big comment block explaining why it does what it does, the reason the programmer decided to take the less obvious route, and instructions for future developers who maintain it.

When I see "this->foo()" my brain needs to pause, if only for a moment, and ask why they are calling a function from the object rather than calling the function directly.

Just the fact that my brain must switch gears for a moment, no matter how small that moment is, that is the issue.

The only reason your brain needs to pause for a moment is because you're not used to the syntax.

I already know that it's pointless and that it's annoying as hell. I'm just wondering where it came from.

Did people really start doing this just for intellisense? I mean JFC, just upgrade to 2012.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

I already know that it's pointless and that it's annoying as hell. I'm just wondering where it came from.

Did people really start doing this just for intellisense? I mean JFC, just upgrade to 2012.

It's the most compelling reason I have. If I type this-> I can then see all methods and members of a class. Type a few more letters, and see all of them with a certain prefix, like get or set. Saves on typing, checking names, and prevents typos.

This cannot work for unqualified names, in part because the set of candidates can be large, and in part because of argument dependent lookup. But when calling a method on a class, you don't want argument dependent lookup.


The point is not that it causes ambiguity or strange behavior.

Your original point was that it causes side effects. Are side effects not ambiguity or strange behavior?

This topic is closed to new replies.

Advertisement