Search

8.14 — Anonymous objects

In certain cases, we need a variable only temporarily. For example, consider the following situation:

In the add() function, note that the sum variable is really only used as a temporary placeholder variable. It doesn’t contribute much -- rather, its only function is to transfer the result of the expression to the return value.

There is actually an easier way to write the add() function using an anonymous object. An anonymous object is essentially a value that has no name. Because they have no name, there’s no way to refer to them beyond the point where they are created. Consequently, they have “expression scope”, meaning they are created, evaluated, and destroyed all within a single expression.

Here is the add() function rewritten using an anonymous object:

When the expression x + y is evaluated, the result is placed in an anonymous object. A copy of the anonymous object is then returned to the caller by value, and the anonymous object is destroyed.

This works not only with return values, but also with function parameters. For example, instead of this:

We can write this:

In this case, the expression 5 + 3 is evaluated to produce the result 8, which is placed in an anonymous object. A copy of this anonymous object is then passed to the printValue() function, (which prints the value 8) and then is destroyed.

Note how much cleaner this keeps our code -- we don’t have to litter the code with temporary variables that are only used once.

Anonymous class objects

Although our prior examples have been with built-in data types, it is possible to construct anonymous objects of our own class types as well. This is done by creating objects like normal, but omitting the variable name.

In the above code, Cents(7) will create an anonymous Cents object, initialize it with the value 7, and then destroy it. In this context, that isn’t going to do us much good. So let’s take a look at an example where it can be put to good use:

Note that this example is very similar to the prior one using integers. In this case, our main() function is passing a Cents object (named cents) to function print().

We can simplify this program by using anonymous objects:

As you’d expect, this prints:

6 cents

Now let’s take a look at a slightly more complex example:

In the above example, we’re using quite a few named Cents values. In the add() function, we have a Cents value named sum that we’re using as an intermediary value to hold the sum before we return it. And in function main(), we have another Cents value named sum also used as an intermediary value.

We can make our program simpler by using anonymous values:

This version of add() functions identically to the one above, except it uses an anonymous Cents value instead of a named variable. Also note that in main(), we no longer use a named “sum” variable as temporary storage. Instead, we use the return value of add() anonymously!

As a result, our program is shorter, cleaner, and generally easier to follow (once you understand the concept).

In fact, because cents1 and cents2 are only used in one place, we can anonymize this even further:

Summary

In C++, anonymous objects are primarily used either to pass or return values without having to create lots of temporary variables to do so. Memory allocated dynamically is also done so anonymously (which is why its address must be assigned to a pointer, otherwise we’d have no way to refer to it).

However, it is worth noting that anonymous objects are treated as rvalues (not lvalues, which have an address) -- therefore, all rules about passing and returning rvalues apply.

It is also worth noting that because anonymous objects have expression scope, they can only be used once. If you need to reference a value in multiple expressions, you should use a named variable instead.

Note: Some compilers, such as Visual Studio, will let you set non-const references to anonymous objects. This is non-standard behavior.

8.15 -- Nested types in classes
Index
8.13 -- Friend functions and classes

106 comments to 8.14 — Anonymous objects

  • HaudreN

    Hello. In the following example are the two anonymous objects removed from the stack after /*1*/ is complete or are they removed when the function they are created in ends?

    • nascardriver

      They die at the end of the `add` call

      The use of a stack isn't standardized. The objects may or may not remain on the stack.

  • hellmet

    In the above snippet of code (excerpt from the lesson), this is what I think is happening.

    (1) allocates memory on the 'main' function stack for a Cents object named centsSum. The function call (to operator+) returns a Cent object. This creates an anonymous object at the return location whose values are copied into centsSum and then the anonymous object is promptly destroyed. In this case, at mark (1), there are 2 copies happening, one from function to main's stack, one from stack to the variable centsSum.

    If I uniform-initialize, a copy is returned to main's stack which overwrites the memory that is used by centsSum, so effectively, only one copy is made and centsSum is updated. This overwrite doesn't break the memory layout or cause undefined behavior as both destination and source object are of the same 'size' and 'shape' in memory.

    Have I gotten this right? Do I need to revisit any chapters? Any wild misconceptions I have here?
    Also, How is the copy happening?

    • nascardriver

      > Have I gotten this right?
      I suspect the lessons still use old rules. Before C++17, 2 copies were created. There were some changes about copy/brace initialization, but I won't look them up because it doesn't matter anymore.
      Since C++17, no copies are created, because line 4 creates an object of the same type as the function's return type. `add` constructs the new `Cents` object directly into `main`'s memory.

      If you remember where you read about about the copy rules, please let me know so I can update it.

      You might also be confused by "anonoymous object" not necessarily meaning that there is an object at all. From a language point of view, line 4 is an anonymous object. It's there, it doesn't have a name, but it's not really there after compilation.

      • hellmet

        > If you remember where you read about the copy rules, please let me know so I can update it.
        Hmmm, I would say the first 4 lessons of chapter 2 (it's there in some of the explanations, especially 2.4), 7.4 (and the lessons around this lesson).
        The copies have been stated, but perhaps a nice summary after chapter 7 (also taking few lessons from chapter 2) on how functions return, how they are actually stored back into main's frame would be great! (since by chapter 7, we learnt C++ only does return by value, learnt about the stack, learnt about scope and user defined datatypes)

        > Since C++17, no copies are created, because line 4 creates an object of the same type as the function's return type. `add` constructs the new `Cents` object directly into `main`'s memory.

        Does this mean that with and post C++17, the 2-copy method (return by value + copy init) and 1-copy method (return by value + uniform init) behave as 0 copies by writing directly into main's stack? Nice, that means I don't have to worry about expensive copies! Also, how do you know all of this! I couldn't even begin to make a search phrase to hit into Google! From where can I get info like this? Stackoverflow is hostile to beginners like me, so I'm left with not much of resources (apart from asking you) to look up stuff :/ Speaking of, I can't express enough how useful and important the website and your kind, patient, thought out, detailed explanations have been! Absolute Gold, I say!

        > From a language point of view, line 4 is an anonymous object. It's there, it doesn't have a name, but it's not really there after compilation.
        I was thinking along the same lines. After all, these are, at the end of the day managed by the compiler so that we can code better.

        Thank you for the clarification! God this was messing with my head for _so_ long! I'll get back if I have any more questions about the nuances, Thank you again!

        • nascardriver

          Thanks for digging up the lessons! What they show isn't related to what's happening here. I just noticed that we're a little too early in the lessons to explain what happens when a value is returned. When a class-type is returned, a copy has to be made. This copy might involve more than just copying the memory of one object to that of another object, which is what makes the elision of the copy important to the programmer (If it was just a memory copy, the coder wouldn't notice a difference).
          You'll learn more about what happens during a return when you get to copy constructors and move semantics. The upcoming chapters will get more difficult, but judging by your interest in the language, I think you're going to make it to the end.

          > that means I don't have to worry about expensive copies!
          Your compiler usually optimizes returns for you. What happened in C++17 is that compilers have to omit the copy in certain situations.

          Unless you explicitly tell your compiler to stop omitting copies, it probably will omit the copies caused by `fn2` even when optimizations are disabled.

          • hellmet

            > What they show isn't related to what's happening here. I just noticed that we're a little too early in the lessons to explain what happens when a value is returned. When a class-type is returned, a copy has to be made.
            Hmm I thought as much. They mostly deal with inbuilt types, so a trivial copy is erm.. graspable. With class objects, not so much.

            > You'll learn more about what happens during a return when you get to copy constructors and move semantics.
            Okays!

            This has to write directly into the callers stack then, Okay, noted!

            > return t; // Can still cause a copy, but probably won't.
            Hmm... Is there any way I can check? Of course, premature optimization is evil, but would be nice to see what's happening!

            And thank you very much for your kind words!

            • nascardriver

              > Is there any way I can check?
              That's difficult with what you know so far. You can try godbolt.org . In the assembly view, look for lines like

              those are copies being made.
              For example here, C++14 with explicitly disabled copy elision https://godbolt.org/z/BhEuxH
              Click on line 16 and press ctrl+f10 to reveal the line in assembly (bottom right).
              There's one copy, then another one in the initialization of `c` and another one in main. You can also see the 3 copies being made in the top right panel.

              but you won't find any when elision is enabled ( https://godbolt.org/z/FbZ9S7 ).

              • hellmet

                Oh My God! So Much detail!
                It's _SO_ clear! One copy in the copy init, one copy during the return into main's stack, one copy during the uniform initialization of the centsSum! (hope I got that right)
                Hmm I also see that these copies always seem to call the function that takes a const ref as a parameter.
                Thank you Very Much! Mind Blown!

  • Michael xd

    Hi Alex.
    i still don't understand one thing.Does any anonymous object have a memory adress or not ?
    And if it doesn't, how next instruction works.

    I thought const references needs an lvalue to work.

    Thank you in advance !

    • Alex

      There are 3 possibilities here:
      * The compiler puts it in memory (yes)
      * The compiler uses a CPU register (no)
      * The compiler optimizes it away completely somehow (no)

      In the case of the above, the compiler would create a temporary object with value 3 (in memory) so that a const reference could be set to it.

  • Nirbhay

    Hello!

    Is 'return Cents(c1.getCents() + c2.getCents());' casting of the result to 'Cents' type?

    Thanks.

    • `c1.getCents() + c2.getCents()` returns an `int`. The `Cents(int)` constructor is used to construct a new `Cents`.
      There's no need to explicitly call the constructor, you can use brace initialization

      • Nirbhay

        I do not understand the following things:

        if we use 'return {c1.getCents() + c2.getCents()};' like you mentioned above then.........

        1. it returns 'int'(As anonymous object) but the function add() says that it should return a 'Cents'.

        2. how do we call 'getCents()' on this 'int' in this line:

        Does it get called on the anonymous object with int type then?

        Thanks.

        • 1.

          Constructs a new `Cents` object, because brace initialization knows the return type.

          2.
          You're not calling it on an `int`, you're calling it on the `Cents` returned by `add`.

  • sekhar

    Hi Alex,

    In Summary, the statement "This means anonymous objects can only be passed or returned by value or const reference." w.r.t returned by value looks good but return by const reference is a problem. statement should be modified i believe. Below code causes segmentation file when returning const reference.

    #include <iostream>
    #include <string>

    using namespace std;

    class Cents
    {
    private:
        int m_cents;
        
    public:

        Cents(int val):m_cents(val){    }
        
        ~Cents() { }
        
        friend const Cents& addCents(const Cents& c1, const Cents& c2);
        
        int getValue() const
        {
            return m_cents;
        }
    };

    const Cents& addCents(const Cents& c1, const Cents& c2)
    {
        return Cents(c1.getValue() + c2.getValue());
    }

    int main()
    {
        Cents c1(2), c2(3);
        cout << "Total Cents :" << addCents(c1, c2).getValue() << endl;
        return 0;
    }

  • Paulo Filipe

    Thanks for this member function Alex

    It made me re-read lesson 8.10 and realize my brain didn't record a damm thing. :)

  • ConfusedKid

    Isn't it somewhat confusing?
    How does the object "Cents" able to getCents??
    I would agree if "Person" is able to getCents and then "Cents" makes it's count less. But it is not alive to give you any "Cents". Jesus

    Just an example of how would I see this happening:

    I do see some possible behavior that cents can exhibit, ex.: being pressed at some random amount, pushed, affected by some kind of chemical reaction.
    But if cents itself doing something with itself, holy crap, correct me if I'm wrong, but how is it possible!!!!!!!!!!!!!
    Even we are as a complex object do what we do because of internal mechanisms that push us to eat, drink, talk etc. Or external conditions that impact on us in order to behave somewhat appropriateable.
    #EDITION
    Or should I think about it as if we say to object like invoke this behavior? object->getMeCents? Wouldn't it be then like Cents->GiveMeCents?

    • Reading this was a wild ride. How about the name "getAmount"?

      • ConfusedKid

        Yeap that would be better. As an imperative way of interaction with an object.
        But I mostly confused should I use methods (name it) in imperative way, like:

        Or in declarative way:

        • The first 2 set the state in the animal. The last 2 read the state from the animal an return it.
          Of course it's up to you how you name your functions, this is just my interpretation of the names.

          • ConfusedKid

            Okay, I meant like if that was the syntax of c++:

            But I get it that both ways are superior. Thanks, mate!

  • Ryan

    How could this be improved?

    • * Line 45, 71, 72: Initialize your variables with uniform initialization. You used direct initialization.
      * Line 9, 29, 45: Limit your lines to 80 characters in length for better readability on small displays.
      * Line 6, 7, 26, 27, 51: Initialize to a specific value (0, 0.0, false)
      * @Storage and @Vector2d are equivalent. Don't repeat yourself.

  • Dev

    Hi Alex!
    Could tell me one thing? "Temporary objects" are the same as "Anonymous objects"?

    • Alex

      Yep, I use the two synonymously. Anonymous objects have no name, which means they can't live longer than an expression (unless you set a reference to them), which makes them temporary in nature.

      Neither are particularly well defined terms, so others may have definitions that vary slightly.

  • Jeff

    Perhaps worth noting here (and hopefully it's addressed later), that returning a copy of the object created on the stack only "magically works" if the compiler-generated copy constructor replicates the object. At least in my limited C++ experience, that copy constructor is a shallow copy, which will fail to replicate non-trivial object structures.

  • Conor

    I think this is the right section to ask this. Say I have 3 functions called Foo(), Boo() and Goo(). Boo() and Goo() both return char*'s, initially we have Foo(Boo()) where Boo() calls Goo(). My question is, is there a memory leak here as Foo() is void? Or does the memory be freed up again once the function finishes executing?

    • It depends on the creation of the char* in question. It will only leak memory if the char* was dynamically allocated, otherwise you'll return a temporary or don't have a problem.

      • Conor

        Goo() creates an IntPtr object from .NET and ToPointer is called on it which is then casted to a char*. So a char* is passed all the way up so I'm guessing its fine as its not dynamically allocated and IntPtr handles its own de-allocation implicitly.

        • Once the object leaves .NET and is casted there should be no way for the runtime to know when the object is last used. Meaning that it's either leaking, because it's never being freed, or the object is destroyed right away, leaving you with a dangling pointer. You do some reading on this, I have no clue about .NET or integration into C++, this is just what I think happens.
          If what I said is true, copy the data as soon as you get it and handle the memory yourself.

  • i don't understand this " Memory allocated dynamically is also done so anonymously (which is why its address must be assigned to a pointer, otherwise we’d have no way to refer to it)." does this mean that anonymous objects is stored in the heap as a dynamic allocated object ?  or you mean that just it works the same as dynamic allocation as it does not have a reference we can't refer to it

Leave a Comment

Put all code inside code tags: [code]your code here[/code]