21

What does this say:

return static_cast<Hasher &>(*this)(key);

?

I can't tell whether *this or key is passed to static_cast. I looked around and found this answer, but unlike what I'm stuck on, there's nothing inside the first pair of parentheses.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
johnsmith
  • 515
  • 5
  • 12
  • 8
    I think the piece of the puzzle you're missing is that `Hasher` has an overloaded "function call operator", and can be used as a function. – molbdnilo May 08 '22 at 10:57
  • You're right that the question you linked has a significant difference: it's creating a temporary object of a class type, not using `static_cast` on an existing object. (The answer there explains why that uses empty (), and the fact that this is `static_cast<>()` explains why the first parens are non-empty.) But in both cases, `operator( something )` of the resulting object is being invoked. – Peter Cordes May 09 '22 at 00:36
  • 1
    If you are unsure about the order of evaluation, just add extra parentheses to disambiguate. – Henri Menke May 09 '22 at 07:38
  • 1
    @HenriMenke if they don't know the order of evaluation, how are they supposed to know where to put the parentheses? – Chris H May 09 '22 at 09:44
  • @ChrisH You don't. But you might get lucky when the compiler only accepts one alternative. – Micha Wiedenmann May 09 '22 at 11:09

2 Answers2

33

The statement is parsed as

return (static_cast<Hasher &>(*this))(key);

So, the argument to static_cast is *this. Then the result of the cast, let's call it x, is used as postfix-expression in a function call with key as argument, i.e. x(key), the result of which is returned.

user17732522
  • 53,019
  • 2
  • 56
  • 105
7

I can't tell whether *this or key is passed to static_cast

If you're unsure, you can just look up the syntax.

In the informal documentation, the only available syntax for static_cast is:

static_cast < new-type > ( expression )

and the same is true in any standard draft you compare.

So there is no static_cast<T>(X)(Y) syntax, and the only possible interpretation is:

  • new-type = Hasher&
  • expression = *this

and the overall statement is equivalent to

Hasher& __tmp = static_cast<Hasher &>(*this);
return __tmp(key);

In the skarupke/flat_hash_map code you linked, the interpretation is that this class has a function call operator inherited from the private base class Hasher, and it wants to call that explicitly - ie, Hasher::operator() rather than any other inherited operator(). You'll note the same mechanism is used to explicitly call the other privately-inherited function call operators.

It would be more legible if it used a different function name for each of these policy type parameters, but then you couldn't use std::equal_to directly for the Equal parameter, for example.

It might also be more legible if it used data members rather than private inheritance for Hasher, Equal etc. but this way is chosen to allow the empty base-class optimization for stateless policies.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • *"The interpretation is that the class has a function call operator inherited from the private base class Hasher"* : **not necessarily**. The class is just **explicitly convertible to `Hasher&`**. It may be due to inheritance or a costume **convertion operator** or both( inheritance from a class with conversion operator). – Red.Wave May 12 '22 at 11:58
  • I followed the link to the [skarupke/flat_hash_map](https://github.com/skarupke/flat_hash_map/) in the question and am explaining how to understand that code. I know there are other possible interpretations of the quoted line in general. Have clarified the distinction. – Useless May 12 '22 at 12:16
  • I mean, you should've mention the broader case of **explicit convertiblity** rather than special case of inheritance which may or may not be the actual case. Otherwise your answer is very descrptive as a whole. – Red.Wave May 12 '22 at 12:22
  • I mean, that doesn't affect how the cast is parsed, which was the original question. I only described the interpretation to give OP some context for understanding this code they're reading, I don't want to write an essay on all the different ways the cast itself can be performed. – Useless May 12 '22 at 12:26
  • Interpreting tha cast as inheritance is simply wrong. – Red.Wave May 12 '22 at 12:38
  • This _specific_ code is explicitly up-casting `this` to a private base class, so I'm not sure what you mean by that. – Useless May 12 '22 at 12:44
  • I don't see anything in the context pointing toward inheritance. I'd be happy, if you could show me where in the context of the question you found an upcast. If it were an upcast, casting by pointer would exactly do the job(`(*static_cast(this))();`). The fact that a reference cast is used points toward the more generic case of explicit convertiblity. – Red.Wave May 12 '22 at 12:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/244691/discussion-between-useless-and-red-wave). – Useless May 12 '22 at 13:08