Topic: too many constructor calls when returning objects?


Author: "J Scott Peter XXXIII i/iii" <scotty@cinenet.dot.net>
Date: 1998/01/12
Raw View
Steve Clamage wrote in message <339EF407.2ED9@Eng.Sun.COM>...
>Justin Grant wrote:
>> Ideally, I'd like to have the efficiency of (2) with the convenience of
>> (1).  Perhaps C++ could have a method for specifying that classes
returned
>> from functions are allocated in the caller but constructed in the
function.
>
>This approach has been suggested before. The short answer is that
>it doesn't buy much compared to the optimizations the compiler
>is already allowed to perform.
>
The compiler may be allowed to perform optimisations, but that doesn't make
them easy.  Hints, or a syntax for directly coding a much more efficient
approach, are desirable.  For this problem, the following approach works
very well.  Instead of:

string strFunc()
{
    string str_local = ...        // Code to build string.
    return str_local;            // Usually results in an extra copy.
}

you say:

string strFunc()
{
    new(that) ...                    // Construct the return value directly.
    return *that;
}

"that" is a special keyword which denotes the return value.  In many
compiler's implementations, space for the return value is actually allocated
on the stack before the function is called, then a hidden pointer to that
space is passed down to the function.  In such cases, "that" points to the
return space.  The compiler can then easily recognise "return *that" as a
null-op.

Microsoft Visual C++ 4.2 actually supports this approach (undocumented),
except that instead of the keyword "that", they use "__$ReturnUdt".

gcc uses a different approach.  You write the function as normal (as in the
first example above), but you modify the prototype to read:

string strFunc() return str_local

The point is that at least two compilers, which are near the forefront of
code generation quality, have found it advantageous to have a means of
specifiying the simpler code more directly.  This is the same principle
behind compound operators and the "inline" keyword.  Why force the compiler
to recognise an idiom before it can generate simpler code?  Just create a
syntax for specifying the simpler code directly.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Brock" <peabody@npcinternational.com>
Date: 1998/01/13
Raw View
J Scott Peter XXXIII i/iii wrote in message
<6990b6$hk9$1@marina.cinenet.net>...
<<
>The compiler may be allowed to perform optimisations, but that doesn't make
>them easy.  Hints, or a syntax for directly coding a much more efficient
>approach, are desirable
<<

There is a way to hint this to the compiler :

std::string f() {

    return std::string("optimize me away");    // hints to compiler to use
"optimize me away" in constructor of object
                                                                         //
recieving value
}

Of course like inline your hint may be ignored, but I have ran tests on mine
(MSVC 5.0) and calls like:

std::string x = f();

generated only one constructor call, where defining f as :

std::string f() {

    std::string named_string;

    return named_string;
}

still only generated two calls.

Of course you may need to work with a named variable in your f() or don't
trust your compiler, in which case you can either declare f() as:

void f( std::string& change_me) { /* do something with change me */ }

-or-

std::auto_ptr<std::string> f()


    return std::auto_ptr<std::string>(new std::string);
}

Both of these methods are notationaly inconvienient, and possibly not an
option for operators.  The first also has the disadvantage that in calls to
f :

    // some code fragment

    std::string s = "some string";

    f(s);    // maybe we should find a better name for f ?

it is not always apparent that the string passed is to be modified.  I like
plain old return by value for conrete types like std::string, because it can
help you to eliminate a great number of unnecessary named variables.

As for larger concrete objects, I am undecided, but leaning toward the smart
pointer in cases where it is really f()'s job to create a new object.
Anyone have any opinions as to the most 'correct' method?  Assuming that we
are dealing with a concrete type, what are the stylistic trade-offs of
passing the location for the new object(via reference or pointer) or
returning a smart pointer to a newly created object?  Perhaps it is a bad
idea to return a smart pointer?
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Steve Clamage <stephen.clamage@Eng.Sun.COM>
Date: 1997/06/11
Raw View
Justin Grant wrote:
>
> Getting the results from functions in C++ can be inefficient.  Here are the
> two common methods:
>
> // method 1: returning a copy of the funciton's local var
> string func_val() ...
>
>
> // method 2: modifying a ref passed into the function
> void func_ref(string& ref) ...
>
> The problem with (1) is that there are one or two extra calls to the copy
> constructor required when the value is returned to the caller...
> The problem with (2) is that the function must depend on the caller to pass
> refs to objects that are in a known state.  ...
>
> Ideally, I'd like to have the efficiency of (2) with the convenience of
> (1).  Perhaps C++ could have a method for specifying that classes returned
> from functions are allocated in the caller but constructed in the function.

This approach has been suggested before. The short answer is that
it doesn't buy much compared to the optimizations the compiler
is already allowed to perform.

Basically the complaint boils down to "I want to use value semantics
but I don't want to pay the price of value semantics." Viewed that
way, you have some choices:

1. Accept the cost of value semantics as in your #1.

2. Don't return values from functions, as in your #2. (Return
nothing, or return a reference to a parameter.)

3. Design so that value semantics are not expensive. For example,
you can in principle use reference-counted classes which are
cheap to copy. The class consists only of a pointer to the
counted data (a "helper" or "proxy" class), and all the functions
related to copies are inline. Often it takes only a few machine
instructions to create or destroy the copy.

You can also use a mix of 1 and 2. The canonical problem is with
operator+ for some type T.
 T operator+(const T& left, const T& right) { ... }
You pretty much have to return a value, since you don't have
a good way to return a pointer or reference, absent garbage
collection.

You can provide a member operator+= which returns a reference:
 T& T::operator+=(const T& right) {
  ... // add right to *this
  return *this;
 }
Instead of writing
 t1 = t2 + t3;
clients of T can write this, which involves no extra copies:
 t1 = t2;
 t1 += t3;

--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Justin Grant" <justin@investorsedge.com>
Date: 1997/06/09
Raw View
Getting the results from functions in C++ can be inefficient.  Here are the
two common methods:

// method 1: returning a copy of the funciton's local var
string func_val()
{
  string retval;
  // perform some actions on retval
  return retval;
}

// method 1: modifying a ref passed into the function
void func_ref(string& ref)
{
  // before we can operate on it, we must make
  // initialize ref
  ref.clear();

  // perform some actions on ref, then return
}

The problem with (1) is that there are one or two extra calls to the copy
constructor required when the value is returned to the caller-- two if the
caller is assigning a variable to the results of the function.  I know that
the standard allows the optimization that allows returning constructor
args, so if I understand this optimization correctly, one of those copy
constructor calls could potentially be eliminated, but that still leaves
one extra constructor call.
The problem with (2) is that the function must depend on the caller to pass
refs to objects that are in a known state.  The function must initialize
the ref before using it, unless the function can "trust" the caller to pass
in objects in a known state, or unless the function's work ends up
initializing the object anyway.

Ideally, I'd like to have the efficiency of (2) with the convenience of
(1).  Perhaps C++ could have a method for specifying that classes returned
from functions are allocated in the caller but constructed in the function.
 A possible syntax could utilize the return keyword, as follows:

// "return(retval("Hello"))" indicates that this function has
// the behavior discussed above, and that "retval"
// is the name of the return value.  "retval" will be
// allocated on caller's stack before function is called,
// constructed by function on entry, but owned
// (and destroyed) by caller
string func ()
 return(retval("Hello"))
{
 retval += "World";

 // we know what the return variable is, so there's no need to sepcify it
here.
 return default;
}
// declaration
string func () return;

I'm sure there are problems with how to fit this proposal into the scenario
where the constructor throws an exception. There are also issues of how to
optimize transitive calls to functions like this T x(func1(func2()))-- I'm
not sure how that would work.  Nevertheless, for programs which pass lots
of small objects back and forth (e.g. if you make heavy use of STL strings)
the savings could be considerable.

Is this idea practical?  Am I missing something?   What's your feedback?

 Justin Grant
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]