Thread: Temporary object return

  1. #1
    Registered User
    Join Date
    Jul 2011
    Posts
    22

    Temporary object return

    I am making various objects in my code that contain a toString() method. This method was originally intended to take in nothing and return a string object. Unfortunately, the obvious downfall occurs from the code below:

    Code:
    Str Obj::toString()
    {
        Str temp;
        temp = "data goes here";
        return temp;
    }
    This would be a very nice way to implement the code, and I do believe this implementation works in Java. However, either it works or not in Java, it does for me in C++. Many people will most likely say to use pointers, but I would rather stay away from that if possible. Instead, my current implementation is listed below:
    Code:
    Str& Obj::toString(Str &temp){
        temp = "data goes here";
        return temp;
    }
    where a temporary variable is passed in from the same scope, therefore it is also released at the same scope it's finished.

    I would really enjoy obtaining this implementation:
    Code:
    myLoggingSystem(Str()<<"This object contains: "<<myObj.toString());
    Any better ideas?

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Quote Originally Posted by jakebird451
    Unfortunately, the obvious downfall occurs from the code below:
    Sorry, but the downfall is not obvious to me. You should state what exactly you find problematic.

    Generally, the conventional approach is to overload operator<< for output streams, e.g.,
    Code:
    std::ostream& operator<<(std::ostream& out, const Obj& obj)
    {
        return out << obj.get();
    }
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    in standard C++, returning a temporary is really about the only way of doing it cleanly.

    if you are using a compiler that supports the new standard (C++2011), you can return an rvalue reference as follows:
    Code:
    Str&& Obj::toString()
    {
        Str temp;
        temp = "data goes here";
        return std::move(temp);
    }
    edit: actually, after testing this, it doesn't work that way either. it crashes on linux with g++ 4.6, so you're stuck returning a temporary by value.
    Last edited by Elkvis; 08-31-2011 at 12:49 PM. Reason: bad code

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    You must not return references to locals, rvalue or not.

    You return local variables by value, and in C++11 this will invoke move constructor/assignment - if the copy is not elided altogether.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  5. #5
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Elkvis, simply returning an r-value reference does not stop it from returning a reference. An r-value reference is still a reference, and you are returning a reference, not to a temporary object, but a local variable. Hence the code is undefined behavior.
    However, there is, in fact, an optimization known something along the lines of NRVO (named return value object) where the compiler can optimize temporary copies created from returning by-value, making it as efficient as passing in a reference.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  6. #6
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Quote Originally Posted by Elysia View Post
    Elkvis, simply returning an r-value reference does not stop it from returning a reference.
    I guess I optimistically hoped that the semantics of returning an r-value reference would allow the object to survive somehow beyond the end of the scope, just long enough to have its data copied. as it turns out, I was incorrect

    I thought of another possibility: make the local variable static. just make sure it gets initialized every time the function is called, and return an r-value reference to it. the r-value assignment operator for the object receiving the return value can move the data from the returned object, and leave it in an empty or default state.

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    This will likely have the same effect as returning a temporary variable. That is, it will only create a more complicated solution for no yield.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    this is true, however, it could be a way to ensure through the use of only standard C++ techniques that you don't have an unnecessary copy operation. if you make sure that the object you're returning has a well-implemented move constructor and move assignment operator, you don't need any special compiler flags.

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You don't need to return an r-value reference to invoke move constructor and move operators.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    you can certainly invoke the move semantics with any kind of return, but only a returned pointer or reference of some sort will eliminate the extra copy operation. if you return the object by value, it gets copied implicitly from the local object to the returned object. the impression I get is that the whole point of this thread is the attempt to prevent unnecessary copying of objects.

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Actually, here is something for you to ponder. First, source:

    Code:
    #include <iostream>
    #include <utility>
    #include <ctime>
    #include <cstdlib>
    
    class A
    {
    public:
    	A(int n) { m_n = n; std::cout << "A: Constructor...\n"; }
    	A(A&) { std::cout << "A: Copy constructor...\n"; }
    	A(A&&) { std::cout << "A: Move constructor...\n"; }
    	~A() { std::cout << "A: Destructor...\n"; }
    
    	int m_n;
    };
    
    A&& foo()
    {
    	static A test(std::rand());
    	return std::move(test);
    }
    
    A bar()
    {
    	A test(std::rand());
    	return test;
    }
    
    int main()
    {
    	std::srand(std::time(nullptr));
    
    	{
    		std::cout << "Test with foo:\n";
    		A tmp = foo();
    		std::cout << "Result: " << tmp.m_n << std::endl;
    	}
    	{
    		std::cout << "\nTest with bar:\n";
    		A tmp = bar();
    		std::cout << "Result: " << tmp.m_n << std::endl;
    	}
    	{
    		std::cout << "\nTest with foo (r-ref):\n";
    		A&& tmp = foo();
    		std::cout << "Result: " << tmp.m_n << std::endl;
    	}
    	{
    		std::cout << "\nTest with bar (r-ref):\n";
    		A&& tmp = bar();
    		std::cout << "Result: " << tmp.m_n << std::endl;
    	}
    }
    Now. Output in debug mode:
    Test with foo:
    A: Constructor...
    A: Move constructor...
    Result: 14197076
    A: Destructor...

    Test with bar:
    A: Constructor...
    A: Copy constructor...
    A: Destructor...
    Result: 14197080
    A: Destructor...

    Test with foo (r-ref):
    Result: 13201

    Test with bar (r-ref):
    A: Constructor...
    A: Copy constructor...
    A: Destructor...
    Result: 2095804
    A: Destructor...
    A: Destructor...

    We see that in this case, foo is clearly more efficient than bar.
    But let's try Release mode first:

    Test with foo:
    A: Constructor...
    A: Move constructor...
    Result: 1833846548
    A: Destructor...

    Test with bar:
    A: Constructor...
    Result: 30160
    A: Destructor...

    Test with foo (r-ref):
    Result: 13625

    Test with bar (r-ref):
    A: Constructor...
    Result: 8450
    A: Destructor...
    A: Destructor...

    Now the tables have changed! Now bar is somehow more efficient than foo!
    And the reason is because the compiler optimized the temporary.

    So no, I do not recommend this approach. It makes the code more obfuscated if anything, with little gain.
    Do not worry about optimizing such small things if there is no need! If the application is running slow, use a profiler and find out why. If it turns out copying is the culprit, then fix it. Otherwise, don't. It can have unintended side effects.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    I know it's supposed to be an example but wouldn't you have to implement move semantics for the move copy to work? I mean, it just prints a message and I don't get how that shows anything.

  13. #13
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    I clearly implemented a move constructor, though?
    Since there are no assignments, no move assignment should be necessary.
    Did I miss something?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  14. #14
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    I'm just very confused since the move copy constructor does nothing, other than print a message.

    What does that really demonstrate?

  15. #15
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    The point is that move constructors are cheaper than copy constructors.
    The easiest example is if you have a class, say vector.

    If you invoke the above example, the vector and its entire contents would be copied to the new object. That means allocating memory, copying contents and finally deallocating the contents of the temporary object. This we're all familiar with. The typical copy constructor.
    But if you use a move constructor, all you'd basically have to do is assign the pointer from the temporary vector to the new vector, set the temporary pointer to nullptr and update various member variables.
    No allocation. No copying. No deallocation.

    I could implement a simple example of that, but I didn't feel that was necessary. Just knowing it invokes the move constructor should tell us that it's cheaper in execution time.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Temporary object created by calling constructor
    By Eman in forum C++ Programming
    Replies: 18
    Last Post: 12-21-2010, 01:19 PM
  2. How do I return a temporary result object?
    By matsp in forum C++ Programming
    Replies: 4
    Last Post: 07-24-2007, 04:41 PM
  3. Temporary object debacle
    By verbity in forum C++ Programming
    Replies: 3
    Last Post: 04-24-2007, 11:37 PM
  4. Temporary object destruction
    By Mortissus in forum C++ Programming
    Replies: 2
    Last Post: 07-05-2006, 06:47 AM
  5. returning a temporary class object
    By nextus in forum C++ Programming
    Replies: 3
    Last Post: 01-31-2003, 03:54 PM

Tags for this Thread