Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the .*& operator do? [duplicate]

Tags:

c++

c++11

stl

I stumbled across this question, which had an answer that used an odd construct:

typedef std::queue<int> Q;
typedef Q::container_type C;

C & get (Q &q)
{
    struct hack : private Q {
        static C & get (Q &q) {
            return q.*&hack::c;
        }
    };
    return hack::get(q);
}

I generally follow that q has access to its own c member that is being referenced by the get function. But, I am at a loss to clearly explain it. What is happening exactly with the .*&, and why is it allowed?

like image 889
jxh Avatar asked Mar 26 '15 22:03

jxh


People also ask

What is the meaning of * symbol?

In English, the symbol * is generally called asterisk. Depending on the context, the asterisk symbol has different meanings. In Math, for instance, the asterisk symbol is used for multiplication of two numbers, let's say 4 * 5; in this case, the asterisk is voiced 'times,' making it “4 times 5”.

What is * used for in texting?

Asterisk. Meaning: You're afraid the person isn't as cool as you. The main reason people use asterisks in a text is to censor a word, for example: "I like deep-fried sandwiches so my friends call me the C*** of Monte Cristo.

What does this mean (*)?

noun. a small starlike symbol (*), used in writing and printing as a reference mark or to indicate omission, doubtful matter, etc. Linguistics. the figure of a star (*) used to mark an utterance that would be considered ungrammatical or otherwise unacceptable by native speakers of a language, as in * I enjoy to ski.

What does the symbol * mean in a form?

“Asterisk is also used in spreadsheet formulas as the mathematical 'times' symbol rather than the instinctive X.” That is something that came way after the real origin. ” * ” has long meant multiplication in real programming languages such as FORTRAN, BASIC, and PASCAL.


2 Answers

typedef std::queue<int> Q;

Q is a queue adapted container.

typedef Q::container_type C;

C is the underlying container of the Q -- which is a deque<int>.

C & get (Q &q) {

get takes a queue and returns a deque. In fact it returns the deque that the queue wraps: by conventional means, this is not possible.

  struct hack : private Q {

hack is a type local to the function. It inherits from Q and has only one static member function. From its name, you may suspect it is a hack. You are right.

No hack is ever instantiated.

    static C & get (Q &q) {

hack::get has the same signature as get itself. In fact we delegate all of the work of get to this method.

      return q.*&hack::c;

this line needs to be broken down. I will do it in more lines:

      using mem_ptr_t = C Q::*; // aka typedef C Q::*mem_ptr_t;
      mem_ptr_t c_mem_ptr = &hack::c;
      C& ret = q.*c_mem_ptr;
      return ret;

The first line defines the type of a member pointer to a field of type C within a Q. Both the C++11 and C++03 ways of naming this type are ugly.

The second line gets a member pointer to the field c in Q. It does this through the hole in the type system of C++. &hack::c is logically of type C hack::* -- a pointer to a member of type C within a class of type hack. In fact, that is why we can access it in a static member of hack. But the c in question is actually in Q, so the actual type of the expression in C++ is C Q::*: a pointer to a member variable of Q.

You cannot directly get this member pointer within hack -- &Q::c is illegal, but &hack::c is not.

You can think of member pointers as 'typed offsets' into another type: &hack::c is the "offset" of c within Q together with knowing it is of type C. Now this isn't really true -- it is some opaque value that tells the compiler how to get c from Q -- but it helps to think about it that way (and it may be implemented that way in simple cases).

We then use this member pointer together with a Q& to get the c out of the Q. Getting a member pointer is constrained by protected: using it is not! The way we do it is with operator .*, which is the member dereference operator, which you can pass either member function pointers or members on the right, and class instances on the left.

instance .* member_ptr is an expression that finds the member "pointed to" by member_ptr within the instance. In the original code, everything was done on one line:

instance .* &class_name::member_name

so it looked like there was an operator .*&.

    }
  };

and then we close up the static method and hack class, and:

  return hack::get(q);
}

call it. This technique gives access to protected state: without it, protected members can only be accessed in child classes of the same instance. Using this, we can access protected members of any instance, without violating any bit of the standard.

like image 63
Yakk - Adam Nevraumont Avatar answered Sep 30 '22 22:09

Yakk - Adam Nevraumont


It's a hack, as the nomenclature indicates.

.* takes an object on the left side, and a member pointer on the right side, and resolves the pointed-to member of the given object. & is, of course, the referencing operator; &Class::Member returns a member pointer, which cannot by itself be dereferenced but which can be used with the .* and ->* operators (the latter being the wackiest of all C++ operators). So obj .* &Class::Member has exactly the same effect as obj.Member.

The reason this more complicated version is being used comes down to a loophole in protection semantics; basically, it allows access to protected members of a base class object, even if the object is not of the same type as the class doing this dirty hack.

Personally, I think the trick is too clever by half. I'd ordinarily* write such code as:

struct hack : private Q {
    static C & get (Q &q) {
        return static_cast<hack &>(q).c;
    }
};

Which is technically slightly less safe, but doesn't obscure what's going on.

.* Well, ordinarily I'd avoid writing such a thing at all. But I literally did this earlier today, so I can't really throw stones.

like image 20
Sneftel Avatar answered Sep 30 '22 23:09

Sneftel