more on tempalates

M

Mark P

A couple template related questions:

1. In the code pasted below, why does the basic version get selected in
the call to foo and the const version get selected in the call to bar?

2. When working with STL output iterators, is there any general advice
on whether they should be passed by value or reference? I have
templated code which takes ostream_iterators or back_insert_iterators
which I inititally chose to pass by reference, but I've run into the
situation analogous to below where I have to duplicate my code to handle
const and non-const references to each iterator type (4 basically
identical implementations there).

3. Related to this, for my code which can take either an
ostream_iterator or a back_insert_iterator, is there any way to tell the
compiler that these two should be treated identically. Each is a model
of Output Iterator, but I can't find any way to express this in the
language. (Note, the code may also take a Back Insertion Sequence so I
can't simply have the template code assume it's argument is an Output
Iterator.)

Regarding 3, I think may best approach may be to use different function
names for the Output Iterator version and the Back Insertion Sequence
version, and let the template assume one of these models.

Thanks for your advice,
Mark


#include <iostream>
#include <vector>

using namespace std;

template <typename T>
void foo (T& t) {cout << "basic version\n";}

template <typename T>
void foo (const vector<T>& t) {cout << "const version\n";}

template <typename T>
void bar (T t) {cout << "basic version\n";}

template <typename T>
void bar (const vector<T>& t) {cout << "const version\n";}


int main ()
{
vector<int> vi;
foo(vi); // output: basic version
bar(vi); // output: const version
}
 
P

Peter_Julian

| A couple template related questions:
|
| 1. In the code pasted below, why does the basic version get selected
in
| the call to foo and the const version get selected in the call to bar?

The compiler is free to choose whether it will call the vector template
or the int template since you did not specify which foo< ? >() template
to use in main(). In the case of foo(), it probably found the vector
template to be more efficient and in the case of bar() it probably
calculated that passing the vector via copy considerably less efficient.
Hence the output you got.

note... missing reference... T& t
template <typename T>
void bar (T t) {cout << "copy vector template\n";}

Get it? You aren't dealing with a basic vs const template fight.
Besides, basic vs const fights don't exist. A programmer needs to tell
the compiler whether its const or not. Otherwise the compiler will
likely generate a diagnostic where it can't decide which template
generation is more efficient or appropriate (in the case you supply both
a const and a non-const version).

#include <iostream>
#include <vector>

template <typename T>
void foo (T& t)
{
std::cout << "foo(), vector template\n";
}

template <typename T>
void foo (const std::vector<T>& t)
{
std::cout << "foo(), int template\n";
}

template <typename T>
void bar (T t)
{
std::cout << "bar(), copy vector template\n";
}

template <typename T>
void bar (const std::vector<T>& t)
{
std::cout << "bar(), int template\n";
}

int main ()
{
std::vector<int> vi;

foo< std::vector< int > >(vi);
foo< int >(vi);
bar< std::vector< int > >(vi);
bar< int >(vi);

return 0;
}

/*
foo(), vector template
foo(), int template
bar(), copy vector template
bar(), int template
*/

<snip>
 
M

Mark P

Peter_Julian said:
| A couple template related questions:
|
| 1. In the code pasted below, why does the basic version get selected
in
| the call to foo and the const version get selected in the call to bar?

The compiler is free to choose whether it will call the vector template
or the int template since you did not specify which foo< ? >() template
to use in main(). In the case of foo(), it probably found the vector
template to be more efficient and in the case of bar() it probably
calculated that passing the vector via copy considerably less efficient.
Hence the output you got.

note... missing reference... T& t
template <typename T>
void bar (T t) {cout << "copy vector template\n";}

Get it?

I'm not sure I do. Surely there are rules which give one template of a
function priority over another, otherwise what's the point in
specialization at all? Are you saying that those rules are unable to
distinguish between the two choices in each case and it's arbitrary that
the compiler opts for one over another since I didn't explicitly specify
the template arguments?

- Mark


You aren't dealing with a basic vs const template fight.
 
U

Ulrich Eckhardt

Mark said:
2. When working with STL output iterators, is there any general advice
on whether they should be passed by value or reference?

They should be cheap to copy, therefore by value.
3. Related to this, for my code which can take either an
ostream_iterator or a back_insert_iterator, is there any way to tell the
compiler that these two should be treated identically.

Those are not iterator categories. 'input iterator' or 'bidirectional
iterator' are such categories....
Each is a model of Output Iterator, but I can't find any way
to express this in the language.

....which you seem aware of. Now, typically, you don't express the iterator
type other than by the way you use it in a function. Other than that, you
only use
template<typename InIterator>
void print( InIterator beg, InIterator end)
{ ... }
i.e. maybe chose the name so it conveys that it is an input iterator but
otherwise leave the the iterator type as a template argument.
Regarding 3, I think may best approach may be to use different function
names for the Output Iterator version and the Back Insertion Sequence
version, and let the template assume one of these models.

I'm not sure, why not use overloading? Why not use a template function?
Maybe if you provided an example you could get better advise.

Uli
 
M

Martin Vejnar

No, the overload resolution is not influenced by the estimated code
effectivity.
I'm not sure I do. Surely there are rules which give one template of a
function priority over another, otherwise what's the point in
specialization at all? Are you saying that those rules are unable to
distinguish between the two choices in each case and it's arbitrary that
the compiler opts for one over another since I didn't explicitly specify
the template arguments?

There are rules. Compiler cannot choose anything arbitrarily.

That said, let's review your code.

template <typename T> void foo (T &);
template <typename T> void foo (const std::vector<T> &);

When you call 'foo(std::vector<int>&)', the templates get specialized
and are both added to the set of candidate functions. Normal overload
resolution is then performed on this set. The result is rather
straightforward and easily understandable:

// Following will be chosen
void foo (std::vector<int> &);

// This requires worse implicit conversion sequence than
// the first function and will not be called.
void foo (const std::vector<int> &);

When it comes to the 'bar' function, normal overload resolution will
fail, since neither function is better than the other.

// The call to 'bar(std::vector<int> &)' is ambigous.
void bar (std::vector<int>);
void bar (const std::vector<int> &);

Try that. Specialize the templates by hand. The code shouldn't compile.

When normal overload resolution fails and the candidate functions are
templates, a _partial function template ordering_ [14.5.5.2] comes to
play. In essence, the most specialized function template is chosen.

// The second function template is more specialised than the first
// and thus shall be selected by the overload resolution.
template <typename T> void bar (T);
 
P

peter koch

Peter_Julian said:
| A couple template related questions:
|
| 1. In the code pasted below, why does the basic version get selected
in
| the call to foo and the const version get selected in the call to bar?

The compiler is free to choose whether it will call the vector template
or the int template since you did not specify which foo< ? >() template
to use in main(). In the case of foo(), it probably found the vector
template to be more efficient and in the case of bar() it probably
calculated that passing the vector via copy considerably less efficient.
Hence the output you got.

Nononono......the compiler is NOT free to choose - and efficiency
certainly hasn't got anything to do with it.
Please do not give advice when you have no idea what you are talking
about.

/Peter

[snip]

/Peter
 
P

peter koch

Peter_Julian said:
| A couple template related questions:
|
| 1. In the code pasted below, why does the basic version get selected
in
| the call to foo and the const version get selected in the call to bar?

The compiler is free to choose whether it will call the vector template
or the int template since you did not specify which foo< ? >() template
to use in main(). In the case of foo(), it probably found the vector
template to be more efficient and in the case of bar() it probably
calculated that passing the vector via copy considerably less efficient.
Hence the output you got.

note... missing reference... T& t
template <typename T>
void bar (T t) {cout << "copy vector template\n";}

Get it? You aren't dealing with a basic vs const template fight.
Besides, basic vs const fights don't exist. A programmer needs to tell
the compiler whether its const or not. Otherwise the compiler will
likely generate a diagnostic where it can't decide which template
generation is more efficient or appropriate (in the case you supply both
a const and a non-const version).

#include <iostream>
#include <vector>

template <typename T>
void foo (T& t)
{
std::cout << "foo(), vector template\n";
}

template <typename T>
void foo (const std::vector<T>& t)
{
std::cout << "foo(), int template\n";
}

template <typename T>
void bar (T t)
{
std::cout << "bar(), copy vector template\n";
}

template <typename T>
void bar (const std::vector<T>& t)
{
std::cout << "bar(), int template\n";
}

int main ()
{
std::vector<int> vi;

foo< std::vector< int > >(vi);
foo< int >(vi);
bar< std::vector< int > >(vi);
bar< int >(vi);

return 0;
}

/*
foo(), vector template
foo(), int template
bar(), copy vector template
bar(), int template
*/

<snip>
 
P

peter koch

Mark said:
A couple template related questions:

1. In the code pasted below, why does the basic version get selected in
the call to foo and the const version get selected in the call to bar?

Because the only difference between the two foo templates is that one
of the foos has a constant parameter. The first template function is a
perfect match, whereas the second one requires a cast to const. The
best match is chosen.
For the bar-case, there is a plain bar and a bar that takes
std::vectors. The one taking vector is considered a better
specialisation even though there it requires a cast to const.
[snip]

/Peter
 
F

Francis Glassborow

Mark P said:
I'm not sure I do. Surely there are rules which give one template of a
function priority over another, otherwise what's the point in
specialization at all? Are you saying that those rules are unable to
distinguish between the two choices in each case and it's arbitrary
that the compiler opts for one over another since I didn't explicitly
specify the template arguments?

It is certainly never arbitrary. As you say, there are rules though
learning them is sometimes a little complicated. However if the rules do
not result in a unique best choice, the compiler will issue and
ambiguity error and hand the problem back to you.
 
M

Mark P

Martin said:
Mark P wrote:

There are rules. Compiler cannot choose anything arbitrarily.

That said, let's review your code.

template <typename T> void foo (T &);
template <typename T> void foo (const std::vector<T> &);

When you call 'foo(std::vector<int>&)', the templates get specialized
and are both added to the set of candidate functions. Normal overload
resolution is then performed on this set. The result is rather
straightforward and easily understandable:

OK, so you've already hit upon a point of confusion for me.

When I write code like this:

vector<int> vi;
foo(vi);

The first sentence you wrote ("When you call...") suggests that I am
calling foo(vector<int>&) as opposed to foo(vector<int>), and as far as
I can tell this distinction ultimately accounts for the difference in
behavior observed below.

Is this how a compiler always interprets a function call? That is,
before any overload resolution and implicit conversion, are the
arguments of a function assumed to be references (to their static types)?

Thanks,
Mark
// Following will be chosen
void foo (std::vector<int> &);

// This requires worse implicit conversion sequence than
// the first function and will not be called.
void foo (const std::vector<int> &);

When it comes to the 'bar' function, normal overload resolution will
fail, since neither function is better than the other.

// The call to 'bar(std::vector<int> &)' is ambigous.
void bar (std::vector<int>);
void bar (const std::vector<int> &);

Try that. Specialize the templates by hand. The code shouldn't compile.

When normal overload resolution fails and the candidate functions are
templates, a _partial function template ordering_ [14.5.5.2] comes to
play. In essence, the most specialized function template is chosen.

// The second function template is more specialised than the first
// and thus shall be selected by the overload resolution.
template <typename T> void bar (T);
 
M

Martin Vejnar

Mark said:
OK, so you've already hit upon a point of confusion for me.

When I write code like this:

vector<int> vi;
foo(vi);

The first sentence you wrote ("When you call...") suggests that I am
calling foo(vector<int>&) as opposed to foo(vector<int>), and as far as
I can tell this distinction ultimately accounts for the difference in
behavior observed below.

Is this how a compiler always interprets a function call? That is,
before any overload resolution and implicit conversion, are the
arguments of a function assumed to be references (to their static types)?

Not really. I merely wanted to emphasise that 'vi' is an lvalue and can
be bound to a non-const reference (if it wasn't an lvalue, the first
'foo' would not participate in the resolution). Sorry for the confusion.

The actual rules used in overload resolution are rather complex. If you
want to get a deeper insight into this subject, you'll have no choice,
but to read through chapter [13.3] of the Standard. Or get a really good
book.

Following paragraph gives the reason for the result of an overload
resolution applied to the above code:

[13.3.3.2/3]
[...]
Standard conversion sequence S1 is a better conversion sequence
than standard conversion sequence S2 if
[...]
- S1 and S2 are reference bindings (8.5.3), and the types to which
the references refer are the same type except for top-level
cv-qualifiers, and the type to which the reference initialized by S2
refers is more cv-qualified than the type to which the reference
initialized by S1 refers. [...]
// Following will be chosen
void foo (std::vector<int> &);

// This requires worse implicit conversion sequence than
// the first function and will not be called.
void foo (const std::vector<int> &);

When it comes to the 'bar' function, normal overload resolution will
fail, since neither function is better than the other.

// The call to 'bar(std::vector<int> &)' is ambigous.
void bar (std::vector<int>);
void bar (const std::vector<int> &);

Try that. Specialize the templates by hand. The code shouldn't compile.

When normal overload resolution fails and the candidate functions are
templates, a _partial function template ordering_ [14.5.5.2] comes to
play. In essence, the most specialized function template is chosen.

// The second function template is more specialised than the first
// and thus shall be selected by the overload resolution.
template <typename T> void bar (T);
template <typename T> void bar (const std::vector<T> &);
 
M

Mark P

Martin said:
Mark said:
OK, so you've already hit upon a point of confusion for me.

When I write code like this:

vector<int> vi;
foo(vi);

The first sentence you wrote ("When you call...") suggests that I am
calling foo(vector<int>&) as opposed to foo(vector<int>), and as far
as I can tell this distinction ultimately accounts for the difference
in behavior observed below.

Is this how a compiler always interprets a function call? That is,
before any overload resolution and implicit conversion, are the
arguments of a function assumed to be references (to their static types)?

Not really. I merely wanted to emphasise that 'vi' is an lvalue and can
be bound to a non-const reference (if it wasn't an lvalue, the first
'foo' would not participate in the resolution). Sorry for the confusion.

The actual rules used in overload resolution are rather complex. If you
want to get a deeper insight into this subject, you'll have no choice,
but to read through chapter [13.3] of the Standard. Or get a really good
book.

Following paragraph gives the reason for the result of an overload
resolution applied to the above code:

[13.3.3.2/3]
[...]
Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if
[...]
- S1 and S2 are reference bindings (8.5.3), and the types to which
the references refer are the same type except for top-level
cv-qualifiers, and the type to which the reference initialized by S2
refers is more cv-qualified than the type to which the reference
initialized by S1 refers. [...]

Funny, I was just looking at that section of the standard an hour ago
trying to make sense of this, and I dismissed that clause as
inapplicable because the types (minus cv-quals) weren't the same, one
being T and the other being vector<T>. But now that you highlight it
for me, I see that following template specialization they are of course
the same.

So that explains the behavior regarding foo-- the standard simply
declares that conversion to T& is better than conversion to const T&.

Regarding bar, where the candidate conversions are to T (not T&) and
const T&, is the following accurate? Both conversions are specified by
the standard as having rank "Exact Match" and therefore neither
conversion is better than the other. Because of this, the more
specialized template is chosen (as you pointed out in an earlier
message, below).

Thanks again for your very clear and helpful explanations.

Regards,
Mark
// Following will be chosen
void foo (std::vector<int> &);

// This requires worse implicit conversion sequence than
// the first function and will not be called.
void foo (const std::vector<int> &);

When it comes to the 'bar' function, normal overload resolution will
fail, since neither function is better than the other.

// The call to 'bar(std::vector<int> &)' is ambigous.
void bar (std::vector<int>);
void bar (const std::vector<int> &);

Try that. Specialize the templates by hand. The code shouldn't compile.

When normal overload resolution fails and the candidate functions are
templates, a _partial function template ordering_ [14.5.5.2] comes to
play. In essence, the most specialized function template is chosen.

// The second function template is more specialised than the first
// and thus shall be selected by the overload resolution.
template <typename T> void bar (T);
template <typename T> void bar (const std::vector<T> &);
 
M

Martin Vejnar

Mark said:
Martin said:
The actual rules used in overload resolution are rather complex. If
you want to get a deeper insight into this subject, you'll have no
choice, but to read through chapter [13.3] of the Standard. Or get a
really good book.

Following paragraph gives the reason for the result of an overload
resolution applied to the above code:

[13.3.3.2/3]
[...]
Standard conversion sequence S1 is a better conversion sequence
than standard conversion sequence S2 if
[...]
- S1 and S2 are reference bindings (8.5.3), and the types to which
the references refer are the same type except for top-level
cv-qualifiers, and the type to which the reference initialized by S2
refers is more cv-qualified than the type to which the reference
initialized by S1 refers. [...]

Funny, I was just looking at that section of the standard an hour ago
trying to make sense of this, and I dismissed that clause as
inapplicable because the types (minus cv-quals) weren't the same, one
being T and the other being vector<T>. But now that you highlight it
for me, I see that following template specialization they are of course
the same.

So that explains the behavior regarding foo-- the standard simply
declares that conversion to T& is better than conversion to const T&.

Regarding bar, where the candidate conversions are to T (not T&) and
const T&, is the following accurate? Both conversions are specified by
the standard as having rank "Exact Match" and therefore neither
conversion is better than the other. Because of this, the more
specialized template is chosen (as you pointed out in an earlier
message, below).

Yes, precisely. They are both "Identities", thus having "Exact Match"
rank. For 'T' it's obvious, for 'const T &' I'd say the following applies:

[13.3.3.1.4/2]
When a parameter of reference type is not bound directly to an argument
expression, the conversion sequence is the one required to convert the
argument expression to the underlying type of the reference according to
13.3.3.1. Conceptually, this conversion sequence corresponds to
copy-initializing a temporary of the underlying type with the argument
expression. Any difference in top-level cv-qualification is subsumed by
the initialization itself and does not constitute a conversion.

The parameter is not bound directly, since parameter's underlying type
and the argument's type are different ('T' and 'const T'). cv-qualifiers
are discarded (as they are not nessesary for the conceptual
copy-initialization). An implicit converision sequence from 'T' to 'T'
is a standard conversion sequence Identity.
Thanks again for your very clear and helpful explanations.

You're welcome. From time to time, it's nice to talk to someone who
takes the time to read the Standard :).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top