Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to fix a program that depends on friend injection?

18 views
Skip to first unread message

Rich Wales

unread,
Mar 11, 2007, 3:09:05 PM3/11/07
to
I'm responsible for maintaining a large C++ program at my school.
Recently, while doing a regression test on this program, I discovered
that it wouldn't compile any more. It had run just fine on an older
GNU C++ compiler (3.4.4), but a newer compiler (4.1.2) gave lots of
"not declared in this scope" errors.

The problem appears to be due to widespread use of friend functions
that are invoked via "friend injection". The program compiles just
fine if I use g++'s "-ffriend-injection" compiler flag; however, since
this flag "may be removed in a future release of G++", I would rather
not depend on it if I don't absolutely have to.

Can anyone point me to a description of techniques for modifying a
program so it won't depend on friend injection? Can the declarations
and/or uses of friend functions be changed in some straightforward way
to make them work without requiring the "-ffriend-injection" flag?

I'm not trying to start another philosophical debate about whether
friend injection is good or bad. And a wholesale rewrite of the
software in question here is simply not feasible -- whatever I do
needs to be a relatively minor change to the existing code, or else
I'll simply have to keep using the "-ffriend-injection" flag and
hope this option stays around for an uncomfortably long time.

Rich Wales ri...@richw.org http://www.richw.org


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

Gennaro Prota

unread,
Mar 11, 2007, 4:07:01 PM3/11/07
to
On Sun, 11 Mar 2007 13:09:05 CST, Rich Wales wrote:

>I'm responsible for maintaining a large C++ program at my school.
>Recently, while doing a regression test on this program, I discovered
>that it wouldn't compile any more. It had run just fine on an older
>GNU C++ compiler (3.4.4), but a newer compiler (4.1.2) gave lots of
>"not declared in this scope" errors.
>
>The problem appears to be due to widespread use of friend functions
>that are invoked via "friend injection". The program compiles just
>fine if I use g++'s "-ffriend-injection" compiler flag; however, since
>this flag "may be removed in a future release of G++", I would rather
>not depend on it if I don't absolutely have to.
>
>Can anyone point me to a description of techniques for modifying a
>program so it won't depend on friend injection? Can the declarations
>and/or uses of friend functions be changed in some straightforward way
>to make them work without requiring the "-ffriend-injection" flag?

It should be as simple as adding a further declaration in the
namespace you want the name to be in (that is, the one in which it
gets injected when using -ffriend-injection). This could help:

<http://en.wikipedia.org/wiki/Barton-Nackman>

For further background, note that it also has a link to the committee
paper (1995) where removal of name injection was being proposed.

Should you still have problems I'd recommend posting a concrete
minimal example.

--
Genny.

James Kanze

unread,
Mar 12, 2007, 10:21:14 AM3/12/07
to
On Mar 11, 9:07 pm, Gennaro Prota <clcppm-pos...@this.is.invalid>
wrote:

> On Sun, 11 Mar 2007 13:09:05 CST, Rich Wales wrote:
> >I'm responsible for maintaining a large C++ program at my school.
> >Recently, while doing a regression test on this program, I discovered
> >that it wouldn't compile any more. It had run just fine on an older
> >GNU C++ compiler (3.4.4), but a newer compiler (4.1.2) gave lots of
> >"not declared in this scope" errors.

> >The problem appears to be due to widespread use of friend functions
> >that are invoked via "friend injection". The program compiles just
> >fine if I use g++'s "-ffriend-injection" compiler flag; however, since
> >this flag "may be removed in a future release of G++", I would rather
> >not depend on it if I don't absolutely have to.

> >Can anyone point me to a description of techniques for modifying a
> >program so it won't depend on friend injection? Can the declarations
> >and/or uses of friend functions be changed in some straightforward way
> >to make them work without requiring the "-ffriend-injection" flag?

> It should be as simple as adding a further declaration in the
> namespace you want the name to be in (that is, the one in which it
> gets injected when using -ffriend-injection). This could help:

> <http://en.wikipedia.org/wiki/Barton-Nackman>

I don't think it's too relevant to his problem. If he were
using the Barton and Nackman trick, ADL would find his
functions; the standard was designed intentionally to ensure
that this worked. (Lucky, too, since the whole point of the
Barton and Nackman trick is that you can't really name the
function outside the template class in which it is a member. At
least, not without knowing what types the template class will be
instantiated over.)

BTW: the Wikipedia article doesn't seem to be correct
technically (not really surprising, for Wikipedia): if memory
serves me correctly, you've always been able to overload
template functions.

--
James Kanze (GABI Software) email:james...@gmail.com
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

--

Daniel Krügler

unread,
Mar 12, 2007, 7:14:24 PM3/12/07
to

James Kanze schrieb:

> On Mar 11, 9:07 pm, Gennaro Prota <clcppm-pos...@this.is.invalid>
> wrote:
> > It should be as simple as adding a further declaration in the
> > namespace you want the name to be in (that is, the one in which it
> > gets injected when using -ffriend-injection). This could help:
>
> > <http://en.wikipedia.org/wiki/Barton-Nackman>
>
> I don't think it's too relevant to his problem. If he were
> using the Barton and Nackman trick, ADL would find his
> functions; the standard was designed intentionally to ensure
> that this worked. (Lucky, too, since the whole point of the
> Barton and Nackman trick is that you can't really name the
> function outside the template class in which it is a member. At
> least, not without knowing what types the template class will be
> instantiated over.)

I'm not from the old school, so here only some guesses
from my part - I hope they don't cause more trouble than
we already have ;-)

1) After reading the Wikipedia quotation of

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1995/N0777.pdf

I have the impression, that during that time name injected
names could be found via ordinary lookup, see the
declaration of

friend char f(char);

on page 3 and its discussion - I wonder how ODR violations
could be prevented under those circumstances.

2) Although I have only little experience with the gcc flags,
the documention I found, see

http://gatekeep.cs.utah.edu/hppd/hpux/Gnu/gcc-4.1.1/man.html

"Inject friend functions into the enclosing namespace, so that
they are visible outside the scope of the class in which they
are declared. Friend functions were documented to work this
way in the old Annotated C++ Reference Manual, and versions
of G++ before 4.1 always worked that way. However, in ISO
C++ a friend function which is not declared in an enclosing
scope can only be found using argument dependent lookup."

strengthens this point of view concerning history.

Regrettably I have not the ARM available, to proof this
hypothesis, but I'm eager to hear about that from those
who have.

Another interesting point is, that according to the N0777
paper the POI during that times was located at a totally
different location compared to nowadays, also see page 3.
If this was the original POI for instantiated class templates
I wonder how local classes went into such a position as
outsider's - During that time most instances ended as
local classes!

> BTW: the Wikipedia article doesn't seem to be correct
> technically (not really surprising, for Wikipedia): if memory
> serves me correctly, you've always been able to overload
> template functions.

It seems that the article just reflects a view, which is
presented in the book "C++ Templates" from Vandevoorde/
Josuttis (Page 174, sect. 11.7 in the 1st edition), which
is also referenced at the end of the article, btw - I don't
know what's the truth behind that.

Greetings from Bremen,

Daniel

Gennaro Prota

unread,
Mar 12, 2007, 7:13:02 PM3/12/07
to
On Mon, 12 Mar 2007 08:21:14 CST, James Kanze wrote:

>> >Can anyone point me to a description of techniques for modifying a
>> >program so it won't depend on friend injection? Can the declarations
>> >and/or uses of friend functions be changed in some straightforward way
>> >to make them work without requiring the "-ffriend-injection" flag?
>
>> It should be as simple as adding a further declaration in the
>> namespace you want the name to be in (that is, the one in which it
>> gets injected when using -ffriend-injection). This could help:
>
>> <http://en.wikipedia.org/wiki/Barton-Nackman>
>
>I don't think it's too relevant to his problem. If he were
>using the Barton and Nackman trick, ADL would find his
>functions; the standard was designed intentionally to ensure
>that this worked.

I know :-) I thought to mention it for completeness of information,
not because it was strictly necessary to fix the problem.

>(Lucky, too, since the whole point of the
>Barton and Nackman trick is that you can't really name the
>function outside the template class in which it is a member. At
>least, not without knowing what types the template class will be
>instantiated over.)
>
>BTW: the Wikipedia article doesn't seem to be correct
>technically (not really surprising, for Wikipedia): if memory
>serves me correctly, you've always been able to overload
>template functions.

I wrote that part. But I wasn't born (C++-wise :-)) at that time; the
claim comes for "C++ Templates: The Complete Guide". I'd be very
surprised if Daveed and Nico got this wrong. (Same about you, in
general, but in this case you say that you haven't checked and are
just recollecting)

--
Genny.

Daveed

unread,
Mar 13, 2007, 4:28:16 AM3/13/07
to
On Mar 12, 7:13 pm, Gennaro Prota <clcppm-pos...@this.is.invalid>
wrote:
> surprised ifDaveedand Nico got this wrong. (Same about you, in

> general, but in this case you say that you haven't checked and are
> just recollecting)


I'm afraid I did get it slightly wrong, actually. Function template
overloading wasn't explicitly outlawed, but there were no partial
ordering rules (I believe even the rule preferring a nontemplate over
a template instance wasn't there yet). That made the use of broadly
generic templates like

template<typename T> bool operator==(T const&, T const&);

impractical.

I corrected the Wikipedia article. (Some corrections I made about a
year ago had mutated already into something inaccurate; I fixed the
basic description aain -- we'll see if it sticks.)

The original poster's problem is indeed that friend name injection is
no longer standard (and hasn't been since the acceptance of N0777 over
a decade ago). The general work-around you suggest -- to ensure that
the entity declared by the friend declaration is also declared in a
global or namespace scope -- is also correct. That work-around is not
needed (and not possible) for friend definitions resulting from the
Barton-Nackman trick.

Daveed


--

Gennaro Prota

unread,
Mar 13, 2007, 2:13:55 PM3/13/07
to

Oh :-) I think it would help to report the fix on the book site; for
readers of the book, of course, *and* for Wikipedia's verifiability
--strictly speaking if someone fixes the article reinserting the
contents from the book he is "correct", as Wikipedia's criterion for
inclusion is verifiability, not truth. Of course if the fix is
mentioned on the book site I can add a link.

>Function template
>overloading wasn't explicitly outlawed, but there were no partial
>ordering rules (I believe even the rule preferring a nontemplate over
>a template instance wasn't there yet). That made the use of broadly
>generic templates like
>
> template<typename T> bool operator==(T const&, T const&);
>
>impractical.
>
>I corrected the Wikipedia article. (Some corrections I made about a
>year ago had mutated already into something inaccurate; I fixed the
>basic description aain -- we'll see if it sticks.)

I guess you are referring to the relation with the CRTP? I wrote that
too :-s Actually I rewrote almost the whole article, so I'm to blame
for 99% of the errors. The fact that it doesn't necessarily *rely* on
the CRTP is inferred from your book as well, where you provide the
example:

template<typename T>
class Array {
public:
...
friend bool operator == (Array<T> const& a,
Array<T> const& b) {
return ArraysAreEqual(a, b);
}
};

>The original poster's problem is indeed that friend name injection is
>no longer standard (and hasn't been since the acceptance of N0777 over
>a decade ago). The general work-around you suggest -- to ensure that
>the entity declared by the friend declaration is also declared in a
>global or namespace scope -- is also correct. That work-around is not
>needed (and not possible) for friend definitions resulting from the
>Barton-Nackman trick.

Yes, that was the "completeness of information" I was hinting at: I
imagined that the OP could wonder --with good reason-- why some names
were not injected while some others (those from Barton-Nackman)
"were". The answer is, of course, that they are *not* injected in the
same sense that other names are when using -ffriend-injection, yet
they can be found by ADL because the committee explicitly tweaked the
ADL rules to that effect.

--
Genny.

James Kanze

unread,
Mar 13, 2007, 2:49:10 PM3/13/07
to
On Mar 13, 12:13 am, Gennaro Prota <clcppm-pos...@this.is.invalid>
wrote:

> On Mon, 12 Mar 2007 08:21:14 CST, James Kanze wrote:

[...]


> >BTW: the Wikipedia article doesn't seem to be correct
> >technically (not really surprising, for Wikipedia): if memory
> >serves me correctly, you've always been able to overload
> >template functions.

> I wrote that part. But I wasn't born (C++-wise :-)) at that time;

But you did verify it with the original documentation from
CFront, didn't you? That would seem a minimum for an
"Encyclopedia". (The fact, of course, that you didn't, and that
you could still write, or modify the article, is symptomatic of
the problem with Wikipedia.)

> the claim comes for "C++ Templates: The Complete Guide". I'd
> be very surprised if Daveed and Nico got this wrong. (Same
> about you, in general, but in this case you say that you
> haven't checked and are just recollecting)

Niether Daveed and Nico, nor myself, are infallible. In this
particular case, however, I'm very sure that some forms of
overloading was supported in CFront 3.0, which is the oldest
implementation of templates. It didn't work at all like what we
have today, but it was still overloading.

Are you sure that David and Nico didn't qualify their statement
with something like: "in its present form". You could definitly
write something like:

int f( int ) ;
template< class T > int f( T ) ;

but it wouldn't work at all like what we expect today.

--
James Kanze (GABI Software) email:james...@gmail.com
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

--

James Kanze

unread,
Mar 13, 2007, 3:55:35 PM3/13/07
to
On Mar 13, 12:14 am, "Daniel Krügler" <daniel.krueg...@googlemail.com>
wrote:
> James Kanze schrieb:

> > > <http://en.wikipedia.org/wiki/Barton-Nackman>

> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1995/N0777.pdf

> friend char f(char);

I'm not sure what you mean by ODR violations; there's no change
there. The only change is look-up. Previously, a name declared
as a friend in a class was "injected" into the next enclosing
scope (or was it the global scope---I forget exactly, and of
course, in pre-namespace days, they were usually the same). So
if you wrote:

class Toto
{
friend char f( char ) ;
} ;

it was the same as if you'd written:

char f( char ) ;
class Toto
{
friend char f( char ) ;
} ;

Roughly speaking, anyway; in pre-standard days, it wasn't always
exactly specified what really happened.

This causes no end of problems when templates are involved, of
course, since the injection occurs at the point of
instantiation. (It probably also caused problems with local
classes.)

> 2) Although I have only little experience with the gcc flags,
> the documention I found, see

> http://gatekeep.cs.utah.edu/hppd/hpux/Gnu/gcc-4.1.1/man.html

> "Inject friend functions into the enclosing namespace, so that
> they are visible outside the scope of the class in which they
> are declared. Friend functions were documented to work this
> way in the old Annotated C++ Reference Manual, and versions
> of G++ before 4.1 always worked that way. However, in ISO
> C++ a friend function which is not declared in an enclosing
> scope can only be found using argument dependent lookup."

> strengthens this point of view concerning history.

> Regrettably I have not the ARM available, to proof this
> hypothesis, but I'm eager to hear about that from those
> who have.

I don't have my copy handy here, either, but it sounds very much
like what I remember.

> Another interesting point is, that according to the N0777
> paper the POI during that times was located at a totally
> different location compared to nowadays, also see page 3.

POI has moved around a lot:-). In pre-standard days, it was
probably best to consider it "unspecified", since compilers
didn't always conform to their own specification anyway:-).

> If this was the original POI for instantiated class templates
> I wonder how local classes went into such a position as
> outsider's - During that time most instances ended as
> local classes!

The problem is that before the standard, there really wasn't any
specification for templates. The CFront implementation was very
experimental (to put it mildly), and the ARM text was written
before it, or about the same time, and couldn't be based on
existing practice, so was also rather "theoretical". Then along
came Borland, and did something completely different than
CFront, and the standards committee went off in yet another
direction.

> > BTW: the Wikipedia article doesn't seem to be correct
> > technically (not really surprising, for Wikipedia): if memory
> > serves me correctly, you've always been able to overload
> > template functions.

> It seems that the article just reflects a view, which is
> presented in the book "C++ Templates" from Vandevoorde/
> Josuttis (Page 174, sect. 11.7 in the 1st edition), which
> is also referenced at the end of the article, btw - I don't
> know what's the truth behind that.

I first experimented with templates using Sun CC 3.0.1, which
was CFront based, with the version number corresponding to the
CFront version. Templates were first introduced in CFront 3.0,
so I rather think it was close to the original implementation.
And there was no problem overloading template functions.

On the other hand, of course, argument deduction worked
considerably differently, as was the actual overload resolution.
For starters, if a non-template function could be called, it
always beat any template function. And template argument
deduction allowed for even less implicit conversions than now.
And there was no notion of "more specialized" or any ordering of
instantiations. And probably a few other differences as well.
In practice, providing two or three different template functions
with the same name probably wouldn't have worked too well. But
providing one template function, along with several non template
functions, with the same name, was not a problem---if you were
happy with the fact that the compiler would call the
non-template function, even with a user defined conversion,
rather than convert, say short to int to call the template
instantiation. (Note that this is all from memory, so I could
easily have gotten some of the details wrong. It is correct in
the main lines, however.)

--
James Kanze (GABI Software) email:james...@gmail.com
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

--

Daniel Krügler

unread,
Mar 13, 2007, 7:11:05 PM3/13/07
to

James Kanze schrieb:

Exactly the template-related situation was what I meant
with ODR-violations. The above quoted function f was part of a
template:

template<class T> class A
friend char f(char);
int g();
};

so the critical situation occurs if we have:

A<int> ai; // OK, f is injected into global scope
A<double> ad; // Ooops, another f is also
// injected into global scope !!

but I think that is what you wanted to express by saying


"This causes no end of problems when templates are involved",

right? And it's the issue that is expressed by Bill Gibbons
when he concluded:

"So we have two instantiations in the same expression with
identical tokens but different types.That is absurd."

> The problem is that before the standard, there really wasn't any
> specification for templates. The CFront implementation was very
> experimental (to put it mildly), and the ARM text was written
> before it, or about the same time, and couldn't be based on
> existing practice, so was also rather "theoretical". Then along
> came Borland, and did something completely different than
> CFront, and the standards committee went off in yet another
> direction.

[..]

> I first experimented with templates using Sun CC 3.0.1, which
> was CFront based, with the version number corresponding to the
> CFront version. Templates were first introduced in CFront 3.0,
> so I rather think it was close to the original implementation.
> And there was no problem overloading template functions.
>
> On the other hand, of course, argument deduction worked
> considerably differently, as was the actual overload resolution.
> For starters, if a non-template function could be called, it
> always beat any template function. And template argument
> deduction allowed for even less implicit conversions than now.
> And there was no notion of "more specialized" or any ordering of
> instantiations. And probably a few other differences as well.
> In practice, providing two or three different template functions
> with the same name probably wouldn't have worked too well. But
> providing one template function, along with several non template
> functions, with the same name, was not a problem---if you were
> happy with the fact that the compiler would call the
> non-template function, even with a user defined conversion,
> rather than convert, say short to int to call the template
> instantiation. (Note that this is all from memory, so I could
> easily have gotten some of the details wrong. It is correct in
> the main lines, however.)

Once again, I have learned lessons from your excellent
memory and very interesting explanation style - thanks!

Greetings,

Daniel

Daveed

unread,
Mar 13, 2007, 11:03:06 PM3/13/07
to
On Mar 13, 2:49 pm, "James Kanze" <james.ka...@gmail.com> wrote:
[...]
> NietherDaveedand Nico, nor myself, are infallible. In this

> particular case, however, I'm very sure that some forms of
> overloading was supported in CFront 3.0, which is the oldest
> implementation of templates.

Theoretically, maybe ;-) However, 3.0 was mostly unusable due to its
extraordinary number of bugs (by then it had moved out of the research
labs and into Unix Systems Labs). The subsequent 3.0.1 release was
the "real" first implementation (the phrase "sucked less" was used for
that point release) which drove some of the first real experience with
templates.

Daveed

Daveed

unread,
Mar 13, 2007, 11:02:27 PM3/13/07
to
On Mar 13, 2:13 pm, Gennaro Prota <clcppm-pos...@this.is.invalid>
wrote:

[...]


> >I'm afraid I did get it slightly wrong, actually.
>
> Oh :-) I think it would help to report the fix on the book site; for
> readers of the book, of course, *and* for Wikipedia's verifiability
> --strictly speaking if someone fixes the article reinserting the
> contents from the book he is "correct", as Wikipedia's criterion for
> inclusion is verifiability, not truth. Of course if the fix is
> mentioned on the book site I can add a link.

I see. I sent a note to Nico (who maintains the errata page)
explaining the error.

> >Function template
> >overloading wasn't explicitly outlawed, but there were no partial
> >ordering rules (I believe even the rule preferring a nontemplate over
> >a template instance wasn't there yet). That made the use of broadly
> >generic templates like
>
> > template<typename T> bool operator==(T const&, T const&);
>
> >impractical.
>
> >I corrected the Wikipedia article. (Some corrections I made about a
> >year ago had mutated already into something inaccurate; I fixed the
> >basic description aain -- we'll see if it sticks.)
>
> I guess you are referring to the relation with the CRTP?

Yes.

> I wrote that
> too :-s Actually I rewrote almost the whole article, so I'm to blame
> for 99% of the errors. The fact that it doesn't necessarily *rely* on
> the CRTP is inferred from your book as well, where you provide the
> example:
>
> template<typename T>
> class Array {
> public:
> ...
> friend bool operator == (Array<T> const& a,
> Array<T> const& b) {
> return ArraysAreEqual(a, b);
> }
> };

Hmm, you're right. That is misleading. The discussion of CRTP later
on is quite explicit that CRTP is part of "the trick" (it's basically
the vehicle to make it reusable). The example you quote was really
meant to explain how the basic mechanism relies on friend name
injection, but as it stands it does a terrible job of actually
defining what constitutes "the trick". Sorry :-(

Daveed


--

Daniel Krügler

unread,
Mar 14, 2007, 11:39:39 AM3/14/07
to
[To the mods: My recent reply did not came through, so
I retry it with one]

On 14 Mrz., 00:11, "Daniel Krügler" <daniel.krueg...@googlemail.com>
wrote:
> James Kanze schrieb:


> > I'm not sure what you mean by ODR violations; there's no change
> > there. The only change is look-up.

[..]

> Exactly the template-related situation was what I meant
> with ODR-violations. The above quoted function f was part of a
> template:
>
> template<class T> class A
> friend char f(char);
> int g();
> };

This example does not properly demonstrate, what I wanted to
show. Here a better one:

template<class T> class A {

friend char f(char) { // Inline definition provided
...
}
};

A<int> ai;
A<double> ad;

AFAIK the two successive instantiations corresponds to
something similar as

inline friend char f(char) {
...
}

inline friend char f(char) {
...
}

which is forbidden according to 3.2/5:

"There can be more than one definition of a [..] inline function
with external linkage (7.1.2) [..] in a program provided that each
definition appears in a different translation unit, [..]"

Of course one could simply specify that implementors have
to get that right (independent from that rule), but what would
happen in this specific case:

template<class T> class A {

friend char f(char) { // Inline definition provided
A<T> instance;
... // Do something with instance
}
};

?

But if I correctly understood your explanation, then neither the
first one nor the second one (with A<T>) would be valid or
would have another meaning, right?

Greetings from Bremen,

James Kanze

unread,
Mar 14, 2007, 11:39:11 AM3/14/07
to
On Mar 14, 12:11 am, "Daniel Krügler" <daniel.krueg...@googlemail.com>
wrote:
> James Kanze schrieb:

> > > friend char f(char);

As long as it's only a declaration, there's no problem here.
And it's only the declaration which is injected, not the
definition.

There could be problems, of course, with the definition if the
function was inline, and it used some symbols whose bindings
changed between the two points of instantiation.

> but I think that is what you wanted to express by saying
> "This causes no end of problems when templates are involved",
> right? And it's the issue that is expressed by Bill Gibbons
> when he concluded:

> "So we have two instantiations in the same expression with
> identical tokens but different types.That is absurd."

I'm not sure. Probably partially. The point is that it works
in very simple cases, but causes no end of problems in more
complicated ones. And even when it "works", it's hard to
specify formally what is actually occuring. Now throw in
namespaces and member templates and what all else, and it's easy
to understand why the committee didn't want to go this route.

--
James Kanze (GABI Software) email:james...@gmail.com
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

--

James Kanze

unread,
Mar 14, 2007, 11:41:09 AM3/14/07
to
On Mar 14, 4:03 am, "Daveed" <vandevoo...@gmail.com> wrote:
> On Mar 13, 2:49 pm, "James Kanze" <james.ka...@gmail.com> wrote:
> [...]

> > Niether Daveed and Nico, nor myself, are infallible. In this


> > particular case, however, I'm very sure that some forms of
> > overloading was supported in CFront 3.0, which is the oldest
> > implementation of templates.

> Theoretically, maybe ;-) However, 3.0 was mostly unusable due to its
> extraordinary number of bugs (by then it had moved out of the research
> labs and into Unix Systems Labs).

Well, it was a .0:-). (At the time, and for a long time
afterwards, it was "common knowledge" that .0 compilers were
unusable. The situation seems to have improved, although I
suspect that that is at least partially due to the fact that
vendors have changed their numbering schemes. It's much easier
to get customers to pay a second time if it is 8.0 rather than
for 7.1 or 7.2.)

> The subsequent 3.0.1 release was
> the "real" first implementation (the phrase "sucked less" was used for
> that point release) which drove some of the first real experience with
> templates.

It's true that my own experience was with Sun CC 3.0.1 (which
was nothing other than CFront 3.0.1); at the time, Sun followed
the CFront numbering exactly, and this was the version which (at
Sun) immediately followed 2.1 (and thus the first Sun compiler
with templates). In general, if I speak of CFront 3.0, this is
the compiler I really mean. (And while it may have "sucked
less" than the real 3.0, it was still a far way from the quality
we find in any of today's compilers.)

--
James Kanze (GABI Software) email:james...@gmail.com
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

--

James Kanze

unread,
Mar 15, 2007, 2:32:05 PM3/15/07
to
On Mar 14, 4:39 pm, "Daniel Krügler" <daniel.krueg...@googlemail.com>
wrote:

> This example does not properly demonstrate, what I wanted to
> show. Here a better one:

> template<class T> class A {
> friend char f(char) { // Inline definition provided
> ...
> }
> };

> A<int> ai;
> A<double> ad;

> AFAIK the two successive instantiations corresponds to
> something similar as

> inline friend char f(char) {
> ...
> }
>
> inline friend char f(char) {
> ...
> }

No. What's injected are the names, not the definitions. In
most ways, you could think of it as the declarations being
injected, but formally, it's not even that: all that happens is
that the name itself is injected; it is thus found during name
lookup in that scope, but it still refers to the declaration
(and possibly definition) in the original class.

> which is forbidden according to 3.2/5:

> "There can be more than one definition of a [..] inline function
> with external linkage (7.1.2) [..] in a program provided that each
> definition appears in a different translation unit, [..]"

> Of course one could simply specify that implementors have
> to get that right (independent from that rule), but what would
> happen in this specific case:

> template<class T> class A {
> friend char f(char) { // Inline definition provided
> A<T> instance;
> ... // Do something with instance
> }
> };

> ?

There you wold have undefined behavior, because you actually
have two different definitions for the (non-template) function.

Note that usually, when this techique is used, the function will
have at least one parameter which depends on the arguments of
the template, e.g.:

template< typename T > class A {
friend char f( T ) { /* ... */ }
...
} ;

In this case, there is no problem, because each instance of the
template refers to (defines) a different overloaded function.

FWIW: g++ (4.1) rejects:

template< typename T > class A {
public:
friend char f( char ) { return 'a' ; }
// ...
} ;

But only if f() is actually used (but then even if the template
is not instantiated). This seems to be intentional (since there
is an option to turn it off, but I can't really find anything
forbidding it in the standard. (On the other hand, the standard
does say that f will be instantiated anytime the template is
instantiated, even if it isn't used, whereas g++ doesn't
complain unless f() is actually used. So there's probably an
error somewhere.)

> But if I correctly understood your explanation, then neither the
> first one nor the second one (with A<T>) would be valid or
> would have another meaning, right?

If by the "first" one, you mean your example with an inline
function not using the template argument in any way, it seems
perfectly valid to me (even though g++ rejects it), but it's
quite possible that I've missed something. (I find the rules
here incredibly complex, and would not be surprised by two
compilers doing different things, with both believing that they
are conformant.)

--
James Kanze (GABI Software) email:james...@gmail.com
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

--

Daniel Krügler

unread,
Mar 16, 2007, 11:05:59 PM3/16/07
to
On 14 Mrz., 00:11, "Daniel Krügler" <daniel.krueg...@googlemail.com>
wrote:
> James Kanze schrieb:

> > I'm not sure what you mean by ODR violations; there's no change
> > there.

[..]

> Exactly the template-related situation was what I meant
> with ODR-violations. The above quoted function f was part of a
> template:
>
> template<class T> class A
> friend char f(char);
> int g();
>
> };
>
> so the critical situation occurs if we have:
>
> A<int> ai; // OK, f is injected into global scope
> A<double> ad; // Ooops, another f is also
> // injected into global scope !!

My reasoning was not very convincing at this place, so lets
retry: I had the following version of A in my mind:

template<class T> class A

friend char f(char) { // (inline) implementation provided
...
}
};

A<int> ai; // OK, f is injected into global scope

A<double> ad; // Ooops, another f definition is injected
// into the same TU

IMO this has (after instantiation of A<int> and A<double>)
a similar effect as this:

inline char f(char) {
...
}

inline char f(char) {
...
}

which is forbidden according to 3.2/5:

"There can be more than one definition of a [..] inline function
with external linkage (7.1.2) [..] in a program provided that each

definition appears in a different translation unit [..]"

Of course one could introduce additional rules which would
simply demand implementors to get that right (e.g. by ignoring
further definitions), but this has to clarified. Problems become
worse, if f was defined as this

template<class T> class A

friend char f(char) { // (inline) implementation provided
A<T> a; // Allowed?
...
}
};

Now it would depend on instantiation orders, which
definition of f is actually generated.

Greetings from Bremen,

James Kanze

unread,
Mar 16, 2007, 11:03:31 PM3/16/07
to
On Mar 13, 9:28 am, "Daveed" <vandevoo...@gmail.com> wrote:
> On Mar 12, 7:13 pm, Gennaro Prota <clcppm-pos...@this.is.invalid>
> wrote:

[...]


> The original poster's problem is indeed that friend name injection is
> no longer standard (and hasn't been since the acceptance of N0777 over
> a decade ago).

Just a nit: friend name injection was never standard; before
1998, there was no such thing as "standard". (I'm not sure
whether it was described in the ARM.)

On the whole, I think that the committee wasn't too afraid to
break with "existing practice" with regards to templates because
most of that "existing practice" post-dated the critical
decisions in committee anyway. (When g++ or Microsoft first
implemented templates, for example, the committee had already
firmly decided on two phase look-up; I wouldn't be surprised, in
fact, if the basic decision pre-dated even the Borland
implementation.)

--
James Kanze (GABI Software) email:james...@gmail.com
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

--

James Kanze

unread,
Mar 17, 2007, 11:48:05 AM3/17/07
to
On Mar 17, 4:05 am, "Daniel Krügler" <daniel.krueg...@googlemail.com>
wrote:

> [..]

> inline char f(char) {
> ...
> }

> inline char f(char) {
> ...
> }

I don't see why. The standard says that the *name* is injected,
not that the complete definition is copied.

> which is forbidden according to 3.2/5:

> "There can be more than one definition of a [..] inline function
> with external linkage (7.1.2) [..] in a program provided that each
> definition appears in a different translation unit [..]"

There is only one definition. That the sequence of tokens
happens to be lexically situated within a template is
irrelevant, and the "definition" is the sequence of tokens.

Of course, it's a little awkward, because the "definition" which
must be unique includes name binding. But I think that the
intent is clear.

> Of course one could introduce additional rules which would
> simply demand implementors to get that right (e.g. by ignoring
> further definitions), but this has to clarified. Problems become
> worse, if f was defined as this

> template<class T> class A
> friend char f(char) { // (inline) implementation provided
> A<T> a; // Allowed?
> ...
> }
>
> };

> Now it would depend on instantiation orders, which
> definition of f is actually generated.

No. I think that's undefined behavior. Although I think that
the standard could be clearer about it, if the template is
instantiated with more than one T, the effect should be as if
there were multiple definitions, because the name binding isn't
the same.

FWIW: g++ refused to instantiate the f at all, claiming "there
are no arguments to 'f' that depend on a template parameter, so
a declaration of 'f' must be available". I would be interested
in some comment by someone concerned with this at g++, because
the decision is obviously intentional: you can force compilation
with -fpermissive, the error message even points this out, and
says that the use of an undeclared name here is deprecated. On
the other hand, I can't find anything to forbid this, even in a
recent draft. (But maybe I'm not looking in the right place.)

With -fpermissive, g++ accepts your function with a warning; the
actual version used does correspond to the first version of the
template instantiated. But as I said, IMHO, this is undefined
behavior.

--
James Kanze (Gabi Software) email: james...@gmail.com


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

--

0 new messages