1

Consider the following C++ code:

class Func {
public:
    void operator()() const { std::cout << this; }
};

int main() {
    Func()(); // OK
    std::cout << &Func(); // Can't take address of a temporary
}

In the body of operator(), it is completely valid to treat this as any other pointer. However, we're told time and again that we can't take the address of a temporary, yet that seems to me to be exactly what's happening. The first line of main is perfectly fine, yet semantically, the line below it is identical. If I were to try to implement this in C to understand what's going on, I might dream up the following (C code):

typedef struct {} Func;

Func construct_Func() {
    Func result;
    return result;
}

void Func_operator_invoke(const Func* const this) {
    printf("%p", this);
}

int main() {
    Func_operator_invoke(&construct_Func()); // Can't take address of a temporary
}

Here, though, we run into a snag: we can't call our "simulated member function" on a temporary... which then begs the question -- why can we call member functions on temporaries in C++? If we can't take the address of a temporary, why can we pass the address of a temporary into a (member) function? Why is that any more valid than passing the address of a temporary into a free function?

Now, I fully expect that the answer to my question is "that's just how it is". That said, let me pose another question to preempt that answer: what does the standard say about calling member functions on temporary objects, and what is a reasonable way for a compiler to implement this? Could it simply push the temporary onto the stack, then pass the pointer to that memory to the function? If so, what's the reasoning behind the C and C++ standards not allowing similar behavior when taking the address of a temporary? As in, why doesn't the C code Func_operator_invoke(&(construct_Func())) simply create an object, then pass a pointer to that object? Is there a published or well-known reason at all?

Joshua Hyatt
  • 1,300
  • 2
  • 11
  • 22
  • 3
    You could consider it a "safety mechanism" because the lifetime of a temporary ends with the expression that created it. So there is no way to have `&Func()` return a valid pointer so the standard forbids it. – UnholySheep Jun 30 '22 at 21:20
  • 5
    This is simply to prevent common mistakes where you'd otherwise store the address in the function you call (which is an error). The same is not expected to occur in a member function. To be clear, temporary objects also have an address. – lorro Jun 30 '22 at 21:21
  • You are 100% correct. You cannot take the address of a temporary. `{ std::cout << this; }` is **not** you taking the address of a temporary. – Eljay Jun 30 '22 at 21:27
  • @UnholySheep This seems to be the consensus... however, in my example, `this` is precisely a pointer to a temporary, and thus in some cases a pointer to temporary is perfectly valid. And this is where "that's just how it is" comes into play – Joshua Hyatt Jun 30 '22 at 21:43
  • 3
    Temporaries have addresses, it is just the use of the `&` operator on an rvalue expression which is restricted, because Bjarne considered that usage was likely to lead to bugs – M.M Jun 30 '22 at 21:50
  • @M.M Could you elaborate? What about something like `3;`, if that temporary integer has an address, what difference does it make to the programmer if you can't take its address? Or are temporaries pushed onto the stack, then popped off at the end of the expression? – Joshua Hyatt Jun 30 '22 at 21:57
  • `3` is a value, not an object. "The stack" is an implementation detail and not part of the language definition; some systems don't have stacks. You can see the address of a temporary with value `3` by `void f(int&& x) { x = 5; cout << &x; }` and calling `f(3)` – M.M Jun 30 '22 at 22:06
  • 2
    The difference it makes is that you are less likely to screw up. Temporaries are _destroyed_ at the end of the expression, generally speaking, so taking the address of one - and then assigning it to something - is going to lead to a dangling pointer. I know that's not what you're doing here, but that is evidently what Bjarne was worried about so he decided it should not be allowed, period. Let's face it, there are enough ways to make holes in your feet with C++ as it is. – Paul Sanders Jun 30 '22 at 22:07
  • @PaulSanders I agree, and this answers one part of the question. The other part was about what the standard has to say about temporaries. As yet, the only person to answer generally missed the mark and failed to answer my questions. – Joshua Hyatt Jun 30 '22 at 22:13
  • I think my comment addresses that - they are destroyed (generally speaking) at the end of the statement that creates them. – Paul Sanders Jun 30 '22 at 22:14
  • @M.M I know that `3` is a value, but what are the rules for creating temporaries? I'd have thought the statement `3;` would create one – Joshua Hyatt Jun 30 '22 at 22:21
  • @PaulSanders I'd be fine with that; would you like to post an answer? Then if I needed additional clarification I could start a discussion in the comments – Joshua Hyatt Jun 30 '22 at 22:22
  • 1
    @JoshuaHyatt Well, technically it creates one and discards it but this is a moot point since the address is not observed (so the compiler doesn't have to assign an address for the temporary) – M.M Jun 30 '22 at 22:25
  • Why would you ever want to print out the value of a pointer to a temporary in the first place? It will be some boring large integer value, somewhere on the stack, so if you’re in the main thread, it will be towards the “end” of the virtual address space (from which the stack grows downwards) somewhere whoknowswhere… What good is it? If you want to print `Func` objects, define a proper overload of `std::ostream& operator <<(std::ostream &out, const Func &func) { ... }` or the like. – Andrej Podzimek Jun 30 '22 at 22:53
  • @JoshuaHyatt I think it's been done to death in the comments, TBH. I'd feel a bit of a fraud taking points for this. – Paul Sanders Jul 01 '22 at 10:56

1 Answers1

3

There's nothing wrong with using this inside a member function. It's a valid pointer while that function is executing. The temporary object is destroyed after at the end of the full-expression (the ;).

So in Func()();

  1. A temporary instance of Func is created
  2. Func::operator() is executed
    • std::cout operator << is executed
  3. The temporary is destroyed

This can't be reproduced with C as C has no classes nor member functions.

With regard to taking an address of a temporary, it's clearly an error as there's no possible valid use for it. For more details refer to Why is taking the address of a temporary illegal?

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • 1
    This sort of sidesteps the spirit of the question... look at my "bad" code: `Func_operator_invoke(&(construct_Func()))` This follows the exact sequence you lay out for `Func()()`: 1) a temporary is created, 2) the function is executed, 3) the temporary is destroyed. Why are two semantically identical expressions treated differently? In answering, look more particularly at my questions in the last paragraph – Joshua Hyatt Jun 30 '22 at 21:33
  • 1
    I think @M.M expressed it best. – Paul Sanders Jun 30 '22 at 21:53
  • 2
    It's not true that there is "no possible valid use" . Just that the language designer felt that it was more likely to be used incorrectly . – M.M Jun 30 '22 at 21:54