C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Template arguments: constant reference vs. pass by value
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Matthias Hofmann
Guest





PostPosted: Thu Jan 06, 2005 11:32 pm    Post subject: Template arguments: constant reference vs. pass by value Reply with quote



Hello eveybody!

Having read quite a few texts on C++ programming, I am convinced that
arguments should not be passed by value, as this may result in costly and
unnecessary calls to the copy constructor and destructor. Instead, one
should pass a constant reference, which is especially true when templates
are involved and the actual type of the object is unknown. For example:

template <class T> void good_func( const T& );
template <class T> void bad_func( T );

With this in mind, I was rather surprised to learn that the STL often seems
to violate this rule. Take the function std::for_each() as an example:

template <class InputIterator, class Function>
Function for_each( InputIterator first, InputIterator last, Function f );

If the passed Function object has a costly copy constructor, there might be
a performance penalty (also the destructor might be expensive).

Actually I started pondering with that matter when I was writing a tree
traversal function. Consider the following code:

// Function to traverse a tree in preorder.
template <class Node, class Function>
Function preorder( Node* node, Function f )
{
if ( node != NULL )
{
f( node->data );

f = preorder( node->left, f );

f = preorder( node->right, f );
}

return f;
}

// Functor to insert each tree element into a list.
template <class T>
struct collector
{
std::list<T> list;

void operator()( T data )
{ list.push_back( data ); }
};

// Demonstrate usage.
void f ( Node* root )
{
collector<int> pre_list =
preorder( root, collector<int>() );
}

The above code is definitely a performance nightmare. The function
"preorder()" recursively traverses a tree, passing a collector<T> object by
value each time. I refuse to believe that this is the way to do it!

For me, the most obvious solution would be passing the Function object by
reference. Of course it cannot be a constant one, as
collector<T>::operator() is not a constant method.

Another way to solve the problem might be a change in the implementation of
collector<T>. It might hold a reference counting smart pointer to a list, so
that only the pointer will be copied, not the entire list.

Any thoughts or comments?

Best regards,

Matthias Hofmann







[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Antoun Kanawati
Guest





PostPosted: Fri Jan 07, 2005 2:59 pm    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote



Matthias Hofmann wrote:
Quote:
Hello eveybody!

Having read quite a few texts on C++ programming, I am convinced that
arguments should not be passed by value, as this may result in costly and
unnecessary calls to the copy constructor and destructor. Instead, one
should pass a constant reference, which is especially true when templates
are involved and the actual type of the object is unknown.

It depends on what you're doing: statistically, you may find that
passing by reference is more frequently approriate. However, that does
not mean that the less frequent mechanism is bad, it just means that it
is appropriate for fewer situations.

[snip]
Quote:
Actually I started pondering with that matter when I was writing a tree
traversal function. Consider the following code:

// Function to traverse a tree in preorder.
template <class Node, class Function
Function preorder( Node* node, Function f )
{
if ( node != NULL )
{
f( node->data );
f = preorder( node->left, f );
f = preorder( node->right, f );
}
return f;
}

// Functor to insert each tree element into a list.
template <class T
struct collector
{
std::list
void operator()( T data )
{ list.push_back( data ); }
};

// Demonstrate usage.
void f ( Node* root )
{
collector<int> pre_list =
preorder( root, collector<int>() );
}

The above code is definitely a performance nightmare. The function
"preorder()" recursively traverses a tree, passing a collector<T> object by
value each time. I refuse to believe that this is the way to do it!

Try this:

// cheaply copyable functor. In low level terms, this is as
// cheap as a pointer.
template <class T>
struct collector {
std::list<T> &accum;

// get it as a const& and let the push_back copy it.
void operator()( const T& data ) { accum.push_back( data ); }

collector(std::list<T> &a) : accum(a) { }
};

std::list<int> accum;

template <class Node, class Function>
void preorder( Node* node, Function f ) {
if ( node ) {
f(node->data);
preorder(node->left, f);
preorder(node->right, f);
}
}

void do_something(node_type *root) {
std::list<int> contents;
preorder(root, collector<int>(contents));
....
}


And, if you don't like reference members:

struct collector {
std::list<T> *accum;

// get it as a const& and let the push_back copy it.
void operator()( const T& data ) { accum->push_back( data ); }

collector(std::list<T> &a) : accum(&a) { }
collcetor(std::list<T> *a) : accum(a) { }
};


Obviously, this sort of class is not meant for creating named instances
that can outlive the reference that they capture. And, there's probably
a boost-based lambda gadget that would express this a lot better.
--
A. Kanawati
[email]NO.antounk.SPAM (AT) comcast (DOT) net[/email]

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
jakacki
Guest





PostPosted: Sat Jan 08, 2005 2:14 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote



Matthias Hofmann wrote:
Quote:
Having read quite a few texts on C++ programming, I am
convinced that arguments should not be passed by value,
as this may result in costly and
unnecessary calls to the copy constructor and destructor.
Instead, one should pass a constant reference, which is
especially true when templates are involved and the
actual type of the object is unknown. For example:

template <class T> void good_func( const T& );
template <class T> void bad_func( T );

It *may* result, but it not always does (char&).
See Boost call_traits.

Quote:
With this in mind, I was rather surprised to learn that
the STL often seems to violate this rule. Take the
function std::for_each() as an example:

template <class InputIterator, class Function
Function for_each( InputIterator first, InputIterator
last, Function f );

If the passed Function object has a costly copy
constructor, there might be a performance penalty (also
the destructor might be expensive).

It seems to me that this is a deliberate choice. Observe,
that passing by reference has different semantics if
operator() is mutating. Moreover, you cannot pass a
temporary by non-const reference, which badly hits
call-site syntax. If you decide to use const reference, you
force const operator() on clients.

Quote:
Actually I started pondering with that matter when I was
writing a tree traversal function. Consider the following
code:

// Function to traverse a tree in preorder.
template Function preorder( Node* node, Function f )
{
if ( node != NULL )
{
f( node->data );

f = preorder( node->left, f );

f = preorder( node->right, f );
}

return f;
}

// Functor to insert each tree element into a list.
template <class T
struct collector
{
std::list
void operator()( T data )
{ list.push_back( data ); }
};

// Demonstrate usage.
void f ( Node* root )
{
collector<int> pre_list =
preorder( root, collector<int>() );
}

The above code is definitely a performance nightmare. The
function "preorder()" recursively traverses a tree,
passing a collector<T> object by value each time. I
refuse to believe that this is the way to do it!

Are you criticizing design of STL or of your code? I don't
think that uniform rules apply. What's OK for STL does not
have to be best fit for you.

Quote:
For me, the most obvious solution would be passing the
Function object by reference.

Depends on what semantics you want to provide. for_each
provides copy semantics for functor, you may provide
reference semantics if you wish so.

My personal experience with tree iterators is that it is
better to implement copy semantics for functors and insist
that they are stateless, providing means for explicit state
passing. It very much simplifies composing recursive
traversal functors.

Quote:
Of course it cannot be a constant one, as
collector<T>::operator() is not a constant method.

Remember that it hits clients, who no longer can pass
a temporary.

Quote:
Another way to solve the problem might be a change in the
implementation of collector<T>. It might hold a reference
counting smart pointer to a list, so that only the
pointer will be copied, not the entire list.

Any thoughts or comments?


Yet another solution would be to change implementation of
preorder so that it uses explicit stack. If your tree is
mutable and has parent links, you can even do without any
stack at all.

BR
Grzegorz

--
Free C++ frontend library: http://opencxx.sourceforge.net
China from the inside: http://www.staryhutong.com
Myself: http://www.dziupla.net/gj/cv




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Maciej Sobczak
Guest





PostPosted: Sat Jan 08, 2005 5:35 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

Hi,

Matthias Hofmann wrote:

Quote:
Having read quite a few texts on C++ programming, I am convinced that
arguments should not be passed by value, as this may result in costly and
unnecessary calls to the copy constructor and destructor.

Except when passing by value is faster than passing by reference. It is,
usually, for those types which are not greater than the size of pointer.
(There is also an interesting issue of the cost of *using* something
that was passed by value vs. by reference. The latter requires
indirection and can be slower.)

Some types commonly used in STL belong to this category, some not. No
matter which strategy you use, you will always penalize somebody.
The assumption in most of STL is that types (iterators, functors, ...)
are cheap to copy. Programmers are obliged to keep this in mind.

Note also that passing by reference may introduce its own problems.
For example, passing by non-const ref means that you cannot use
temporary object as an argument. On the other hand, passing by const ref
means that you cannot modify the parameter inside the function (iterators!).
With all this in mind, the STL way is probably the best we can get.

--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
L.Suresh
Guest





PostPosted: Sat Jan 08, 2005 5:39 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

STL function objects are designed to be passsed by value, and the onus
in on the programmer to ensure that copying has the right semantics. In
particular the programmer should assume that the STL functions may take
a copy of the functor and implement the copy properly. The solution you
said in the end was right. You can have the pointer to a large data in
the functor, so that copying functors are cheap.

It may seem that we can pass by reference, by explicitly specifying
reference argument may work, like

std::foreach<std::vector(...);

this ensures that the functor is passed by reference. but this is not
recommended because the STL implementation may pass your functor to
other templates that may take a copy of your functor.

--lsu


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Sat Jan 08, 2005 6:02 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

Matthias Hofmann wrote:

Quote:
Having read quite a few texts on C++ programming, I am
convinced that arguments should not be passed by value, as
this may result in costly and unnecessary calls to the copy
constructor and destructor. Instead, one should pass a
constant reference, which is especially true when templates
are involved and the actual type of the object is unknown. For
example:

template <class T> void good_func( const T& );
template <class T> void bad_func( T );

It depends. If the function is going to modify the object, and
the semantics require that the object in the caller are not
modified, a copy will be needed anyway; if the caller passes a
temporary, it may even be faster to pass by value.

Quote:
With this in mind, I was rather surprised to learn that the
STL often seems to violate this rule. Take the function
std::for_each() as an example:

template <class InputIterator, class Function
Function for_each( InputIterator first, InputIterator last, Function
f );


Quote:
If the passed Function object has a costly copy constructor,
there might be a performance penalty (also the destructor
might be expensive).

Right.

On the other hand, if I pass by reference, should it be a const
reference, or a non-const reference. If I want to modify the
Function object each time operator() is called, it should be
non-const, but if I want to pass a temporary, it must be const.

It's not an easy choice, even here where the function itself
(for_each) doesn't modify the object. (Given the semantics of
for_each, I would argue that pass by non const reference would
be more appropriate, even if it does exclude the use of a
temporary object. But that's less obvious for things like
Predicate, and orthogonality is a strong arguement in itself.)

Quote:
Actually I started pondering with that matter when I was
writing a tree traversal function. Consider the following
code:

// Function to traverse a tree in preorder.
template Function preorder( Node* node, Function f )
{
if ( node != NULL )
{
f( node->data );

f = preorder( node->left, f );

f = preorder( node->right, f );
}

return f;
}

// Functor to insert each tree element into a list.
template <class T
struct collector
{
std::list
void operator()( T data )
{ list.push_back( data ); }
};

// Demonstrate usage.
void f ( Node* root )
{
collector<int> pre_list =
preorder( root, collector<int>() );
}

The above code is definitely a performance nightmare. The
function "preorder()" recursively traverses a tree, passing a
collector<T> object by value each time. I refuse to believe
that this is the way to do it!

There aren't many other alternatives.

Quote:
For me, the most obvious solution would be passing the
Function object by reference. Of course it cannot be a
constant one, as collector<T>::operator() is not a constant
method.

Which means that you have reference semantics, and not value
semantics.

For an InputIterator (such as in for_each), this is almost
acceptable. For things like find_first_of or find_end, I rather
think not.

Quote:
Another way to solve the problem might be a change in the
implementation of collector<T>. It might hold a reference
counting smart pointer to a list, so that only the pointer
will be copied, not the entire list.

And what happens in a function like find_end, where the
algorithm copies the iterator so that it can make several passes
over it. At the very least, you need copy on write, and given
that most of the time, when the algorithm copies, it immediately
modifies one of the two objects, you're likely not to gain
anything anyway.

If you can limit yourself to an InputIterator, something along
these lines can be used. But only if you limit yourself to
InputIterator (or OutputIterator?).

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Irek SZCZESNIAK
Guest





PostPosted: Sat Jan 08, 2005 6:03 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

Matthias Hofmann wrote:

Quote:
Having read quite a few texts on C++ programming, I am convinced
that arguments should not be passed by value, as this may result in
costly and unnecessary calls to the copy constructor and
destructor. Instead, one should pass a constant reference, which is
especially true when templates are involved and the actual type of
the object is unknown. For example:

template <class T> void good_func( const T& );
template <class T> void bad_func( T );

With this in mind, I was rather surprised to learn that the STL
often seems to violate this rule. Take the function std::for_each()
as an example:

template <class InputIterator, class Function
Function for_each( InputIterator first, InputIterator last, Function f );

If the passed Function object has a costly copy constructor, there
might be a performance penalty (also the destructor might be
expensive).

As to STL, I was surprised that STL (the STL implementation that comes
with GNU 3.3.2, but the SUN and SGI implementations behave in a
similar way) makes lots of functor copies, because it passes functors
by value. For instance, std::sort on avarage makes 1313 functor
copies when sorting an array of 1000 elements. Or,
std::priority_queue makes 8001 copies when your first push into it
1000 random elements and then pop them all.

More on this:

http://groups-beta.google.com/group/comp.lang.c++/browse_frm/thread/8ca3e1d6e1078642/8b0a52941ceaf9b8?tvc=1&q=overhead+of+using+std::sort&_done=%2Fgroups%3Fq%3Doverhead+of+using+std::sort%26&_doneTitle=Back+to+Search&scrollSave=&&d#8b0a52941ceaf9b8

Quote:
(...)

For me, the most obvious solution would be passing the Function
object by reference. Of course it cannot be a constant one, as
collector

Why don't you define your function to pass a non-const reference?
Like this:

template <class Node, class Function>
Function preorder( Node* node, Function & f )

And then use "preorder" like this:

c = collector<int>()

collector<int> pre_list = preorder( root, c );

Whis this new definition of "preorder", "preorder(root,
collector<int>)" would not compile, because you cannot bind a
temporary object to a non-const reference, so you need to create the
object "c".

Quote:
Another way to solve the problem might be a change in the
implementation of collector<T>. It might hold a reference counting
smart pointer to a list, so that only the pointer will be copied,
not the entire list.

Aside from changing the definition of the "preorder" function, this
solution of yours is the only one I know of. The Boost library has
Ref, Function and Bind libraries that can help you. As far as I know
the underlying principle they use is that you create another object
that just holds a pointer or a reference to your object. Please
consult their documentation for more information.

Because I have not used Boost's Ref, Function or Bind extensively I
will not speculate how to use them in your case. But I used Boost
when I wanted to sort a vector of integers according to my functor.
This functor was expensive to copy and I wanted to make sure STL made
no copies, so I used Boost this way:

std::sort(v.begin, v.end(),
boost::function2<bool, int, int>(boost::ref(my_functor)))


Best,
Irek
________________________________________
Irek Szczesniak
http://www.iitis.gliwice.pl/~iszczesniak

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Alberto Barbati
Guest





PostPosted: Sat Jan 08, 2005 11:36 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

Matthias Hofmann wrote:
Quote:
Hello eveybody!

Having read quite a few texts on C++ programming, I am convinced that
arguments should not be passed by value, as this may result in costly and
unnecessary calls to the copy constructor and destructor. Instead, one
should pass a constant reference, which is especially true when templates
are involved and the actual type of the object is unknown. For example:

template <class T> void good_func( const T& );
template <class T> void bad_func( T );

With this in mind, I was rather surprised to learn that the STL often seems
to violate this rule. Take the function std::for_each() as an example:

template <class InputIterator, class Function
Function for_each( InputIterator first, InputIterator last, Function f );

If the passed Function object has a costly copy constructor, there might be
a performance penalty (also the destructor might be expensive).

In practice it happens most frequently that iterators are light objects
and not heavy objects. For example, if T happens to be a plain pointer,
then passing it by-reference, instead of by-value, might produce less
performant code. Even in this non-trivial case, recent compilers can
(sometimes) do miracles when optimizing code. For example, on certain
implementations, if a UDT has the same size of a pointer or two and has
trivial copy ctor (a case that happens frequently for iterator classes)
then passing it by value could be more efficient than passing it by
reference. If the function is eventually expanded inline, then the
difference between passing by-reference and by-value may practically vanish.

On a side note, there was a discussion on Boost about the Alexandrescu's
"Lying const" case. It happens frequently that a function is going to
need a modifiable copy of one of its parameter. For example:

void f1(const T& x) // lying const!
{
T t(x); // need a modifiable copy of x to work with
// ...
}

is programmatically equivalent to:

void f2(T t)
{
// ...
}

IIRC the point was that the compiler has better optimization
opportunities for the f2() than for f1, but I don't exactly remember the
reason.

Notice that for_each is a case of "lying const", at least for the first
of the two parameters.

Quote:
Actually I started pondering with that matter when I was writing a tree
traversal function. Consider the following code:

// Function to traverse a tree in preorder.
template Function preorder( Node* node, Function f )
{
if ( node != NULL )
{
f( node->data );

f = preorder( node->left, f );

f = preorder( node->right, f );
}

return f;
}

// Functor to insert each tree element into a list.
template <class T
struct collector
{
std::list
void operator()( T data )
{ list.push_back( data ); }
};

// Demonstrate usage.
void f ( Node* root )
{
collector<int> pre_list =
preorder( root, collector<int>() );
}

The above code is definitely a performance nightmare. The function
"preorder()" recursively traverses a tree, passing a collector<T> object by
value each time. I refuse to believe that this is the way to do it!

I cannot but agree with you. The problem is not passing parameters by
value, but the fact that you put the list inside the collector class.
Why didn't you just use a back_inserter? You would have gained in both
performances and generality:

template <class Node, class OutIt>
void preorder(Node* node, OutIt it)
{
if (node)
{
*it++ = node->data;
preorder(node->left, it);
preorder(node->right, it);
}
}

void f(Node* root)
{
// preorder to list
std::list<int> list;
preorder(root, std::back_inserter(list));

// preorder to cout
preorder(root, std::ostream_iterator<int>(std::cout, " "));
}

Quote:
Another way to solve the problem might be a change in the implementation of
collector<T>. It might hold a reference counting smart pointer to a list, so
that only the pointer will be copied, not the entire list.

A back inserter iterator is usually implemented as a plain pointer to
the controlled container, so it's very cheap to pass it around by value.
There no real need to use a reference counted implementation.

HTH,

Alberto

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Andrew Koenig
Guest





PostPosted: Sat Jan 08, 2005 10:54 pm    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

"Matthias Hofmann" <hofmann (AT) anvil-soft (DOT) com> wrote


Quote:
Having read quite a few texts on C++ programming, I am convinced that
arguments should not be passed by value, as this may result in costly and
unnecessary calls to the copy constructor and destructor. Instead, one
should pass a constant reference, which is especially true when templates
are involved and the actual type of the object is unknown.

If you ever copy the argument, passing it by reference can sometimes cause a
subtle pitfall.

Consider the following:

template<class T> T f(const T& t) { return t; }

template<class T> T g(T t) { return t; }

You would think that these functions would have the same effect, except that
f copies its argument once and g copies it twice. However, consider:

f("foo");
g("foo");

The call to f is ill-formed, because T is "char[4]" and char arrays can't be
copied. Worse, some compilers implement copying of arrays as an extension,
which means that the call to f will compile on some compilers and not on
others.

The call to g is fine, because T will be cound to const char*. The pointer
representing the address of the initial character of "foo" will be copied.

The std::pair template in C++1998 exhibited this problem. One consequence
was that the following was ill-formed:

std::map<int, std::string> m;

m.insert(make_pair(42, "foo"));

because the call to make_pair would fail like the call to f above. Again,
this failure was particular insidious because some compilers would quietly
accept it.

The C++2003 standard changed the definition of std::pair to take its
constructor arguments by value for precisely this reason.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
dtmoore
Guest





PostPosted: Mon Jan 10, 2005 8:09 pm    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

Yuck! It seems to me that this is a legacy problem inherited from C,
which fails to make an important distinction between things that are
logically containers (i.e. built-in arrays) and things that are
logically iterators (i.e. pointers). IMHO this is a clear defect,
although I certainly see why it would be damn hard to get rid of at
this stage in the game. Unfortunately, it also seems to be having an
isidious effect on modern C++ as well, for example by burdening the
std::pair ctor with an unneccssary copy operation (i.e. pass by value).
Admittedly, it will probably not have a noticable effect in this case,
because objects that are expensive to copy will probably be handled by
reference in std::pair objects in user code. Still, it seems very
ugly.

Also, can someone please elucidate why, in Andrew's example, function f
resolves the deduced type of "foo" as char[4], while g resolves it as
const char *? I *know* that it is defined that way by the standard,
but can someone explain why that is necessary or desired behavior
please? Thanks,

Dave Moore


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Sergey P. Derevyago
Guest





PostPosted: Tue Jan 11, 2005 11:11 pm    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

Andrew Koenig wrote:
Quote:
The std::pair template in C++1998 exhibited this problem. One consequence
was that the following was ill-formed:

std::map<int, std::string> m;

m.insert(make_pair(42, "foo"));

IMHO the code above is not as cute as it seems. Personally, I prefer the

value_type approach for some obvious reasons:

typedef std::map<int, std::string> mymap_type;

mymap_type m;
m.insert(mymap_type::value_type(42, "foo"));

Quote:
because the call to make_pair would fail like the call to f above.

But this use of make_pair doesn't seem to be defensible. So did the standard

really have to be changed?

Quote:
Again,
this failure was particular insidious because some compilers would quietly
accept it.

The C++2003 standard changed the definition of std::pair to take its
constructor arguments by value for precisely this reason.
--

With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Matthias Hofmann
Guest





PostPosted: Wed Jan 12, 2005 9:03 pm    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

"dtmoore" <dtmoore (AT) rijnh (DOT) nl> schrieb im Newsbeitrag
news:1105361193.117888.173090 (AT) c13g2000cwb (DOT) googlegroups.com...

[snip]

Quote:

Also, can someone please elucidate why, in Andrew's example, function f
resolves the deduced type of "foo" as char[4], while g resolves it as
const char *? I *know* that it is defined that way by the standard,
but can someone explain why that is necessary or desired behavior
please? Thanks,


I am taking a wild guess now, but I can imagine that sometimes the number of
characters in the array is required. See the following code:

#include <iostream>

using std::cout;
using std::endl;

template <class T> void f( const T& t ) { cout << sizeof ( t ) << endl; }
template
int main()
{
// Output: 25
f( "templates drive me crazy" );

// Output: 4
g( "templates drive me crazy" );

return 0;
}

Someone might write a function as follows:

template {
T t2;
memcpy( t2, t, sizeof (T) );
cout << t2 << endl;
}

A local copy of 't' can be made because its size is known. This would not be
the case if PrintCopy() would take its argument by value.

By the way, on Visual C++ 6.0 the deduced type for g() is resolved as
'char*' instead of 'cont char*'. I guess that this is wrong?

Best regards,

Matthias Hofmann




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Matthias Hofmann
Guest





PostPosted: Thu Jan 13, 2005 10:00 pm    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

"Sergey P. Derevyago" <non-existent (AT) iobox (DOT) com> schrieb im Newsbeitrag
news:41E2ADAA.5F3FFC06 (AT) iobox (DOT) com...
Quote:
Andrew Koenig wrote:
The std::pair template in C++1998 exhibited this problem. One
consequence
was that the following was ill-formed:

std::map<int, std::string> m;

m.insert(make_pair(42, "foo"));

IMHO the code above is not as cute as it seems. Personally, I prefer the
value_type approach for some obvious reasons:

typedef std::map<int, std::string> mymap_type;

mymap_type m;
m.insert(mymap_type::value_type(42, "foo"));

because the call to make_pair would fail like the call to f above.

But this use of make_pair doesn't seem to be defensible. So did the
standard
really have to be changed?


There might be situations where a char* is preferred towards a std::string,
so why should it have been a bad idea to change the standard in such a was
that it works? I you really want the char* to be changed into a std::string,
then you can write:

m.insert(make_pair<int,std::string>(42, "foo"));

I think Andrew Koenig was aware of the fact that the char* would not change
into a std::string in his example. I guess he wanted to show what happens
when you pass a string literal as a reference.

Best regards,

Matthias Hofmann




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Sergey P. Derevyago
Guest





PostPosted: Sat Jan 15, 2005 2:58 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

Matthias Hofmann wrote:
Quote:
There might be situations where a char* is preferred towards a std::string,

Sure.


Quote:
so why should it have been a bad idea to change the standard in such a was
that it works?

The point is that for map<Key,T> the value_type is defined as

pair<const Key, T> while that call to make_pair() will give you pair<Key, T>.
I.e. some special actions must be taken in order to get _const_ Key with
make_pair().
So why don't we use value_type which is guaranteed to have the correct type?
Why the standard has to support sloppy programming?
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Fuz
Guest





PostPosted: Sat Jan 15, 2005 3:11 am    Post subject: Re: Template arguments: constant reference vs. pass by value Reply with quote

I feel the need to point out a couple of pertinent issues which
no-one has raised. As a game developer, these issues are very much a
problem for us and have cropped up in 'real code'.

First, passing by reference isn't guaranteed to improve efficiency
because of caching. Having a reference can potentially cause a cache
miss on every access to the object, which would be far slower than a
single one-off copy. Of course, it depends on the nature of the object
being copied, how well your compiler copes with generating
cache-friendly code, frequency and locality of the object access
compared to other memory access etc.

Second, passing by value of 'specially aligned' types is actually
illegal in VC++. Take the following example:

__declspec(align(16)) struct matrix4x4
{
float f[4][4];
};

void transform(const matrix4x4& r); // Ok
void transform(matrix4x4 m); // error C2719: 'm': formal parameter with
__declspec(align('16')) won't be aligned

matrix4x4 needs to be aligned as the D3D maths libraries perform
better on matrices placed at 128-bit boundaries. However, VC++ will
not let you pass these types by value, you need to pass them by
reference.

This problem also prevents us from using standard containers of
matrices, or objects containing matrices, because similar
'optimisations' are done in the standard libraries, e.g.
std::vector::resize(size_type, T). This, as well as other
requirements, prompted us to develop our own set of standard-compatible
containers that did not have this problem.

Matthias Hofmann wrote:
Quote:
Hello eveybody!

Having read quite a few texts on C++ programming, I am convinced that
arguments should not be passed by value, as this may result in costly
and
unnecessary calls to the copy constructor and destructor. Instead,
one
should pass a constant reference, which is especially true when
templates
are involved and the actual type of the object is unknown. For
example:

template <class T> void good_func( const T& );
template <class T> void bad_func( T );

With this in mind, I was rather surprised to learn that the STL often
seems
to violate this rule. Take the function std::for_each() as an
example:

template <class InputIterator, class Function
Function for_each( InputIterator first, InputIterator last, Function
f );

If the passed Function object has a costly copy constructor, there
might be
a performance penalty (also the destructor might be expensive).

Actually I started pondering with that matter when I was writing a
tree
traversal function. Consider the following code:

// Function to traverse a tree in preorder.
template Function preorder( Node* node, Function f )
{
if ( node != NULL )
{
f( node->data );

f = preorder( node->left, f );

f = preorder( node->right, f );
}

return f;
}

// Functor to insert each tree element into a list.
template <class T
struct collector
{
std::list
void operator()( T data )
{ list.push_back( data ); }
};

// Demonstrate usage.
void f ( Node* root )
{
collector<int> pre_list =
preorder( root, collector<int>() );
}

The above code is definitely a performance nightmare. The function
"preorder()" recursively traverses a tree, passing a collector<T
object by
value each time. I refuse to believe that this is the way to do it!

For me, the most obvious solution would be passing the Function
object by
reference. Of course it cannot be a constant one, as
collector
Another way to solve the problem might be a change in the
implementation of
collector<T>. It might hold a reference counting smart pointer to a
list, so
that only the pointer will be copied, not the entire list.

Any thoughts or comments?

Best regards,

Matthias Hofmann







[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.