Function declared before call but defined after - compilation fails

J

johnbrown105

Hello All,

I am doing another exercise (I repeat, *exercise*). The (irrelevant to
this
discussion) point is to show that "You can inject a friend declaration
into
a namespace by declaring it within an enclosed class". I have done
this
successfully, but please consider the following program:

//: C10:FriendInjection.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
namespace Me {
class Us {
//...
public:
friend void you();
};
}
// 1
void Me::you(){}

int main() {
Me::you();
return 0;
}

// 2
// defining you() after main() does not compile
// void Me::you(){}
///:~
/////////////////////////////////////////////////////////////////////////

If Me::you() is *defined* at (2) after the call in main(), gcc 4.1.1
says:
"error: 'you' is not a member of 'Me'". If, instead, Me::you is
*defined* at (1),
then it compiles.

MSVC++ Express compiles it either way without a problem, as expected.

Surely this is a bug in gcc?
 
H

Howard

Hello All,

I am doing another exercise (I repeat, *exercise*). The (irrelevant to
this
discussion) point is to show that "You can inject a friend declaration
into
a namespace by declaring it within an enclosed class". I have done
this
successfully, but please consider the following program:

//: C10:FriendInjection.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
namespace Me {
class Us {
//...
public:
friend void you();
};
}
// 1
void Me::you(){}

int main() {
Me::you();
return 0;
}

// 2
// defining you() after main() does not compile
// void Me::you(){}
///:~
/////////////////////////////////////////////////////////////////////////

If Me::you() is *defined* at (2) after the call in main(), gcc 4.1.1
says:
"error: 'you' is not a member of 'Me'". If, instead, Me::you is
*defined* at (1),
then it compiles.

MSVC++ Express compiles it either way without a problem, as expected.

Surely this is a bug in gcc?

Why?

Declaring a function as a friend does not declare or define that function.
It merely states that said function is a friend.

In case (1), you're declaring *and* defining Me::you(). If you move that to
after main() as in case (2), then when main() is compiled, Me::you() has not
yet been declaredand so it's an error.

-Howard
 
J

JLS

Hello All,

I am doing another exercise (I repeat, *exercise*). The (irrelevant to
this
discussion) point is to show that "You can inject a friend declaration
into
a namespace by declaring it within an enclosed class". I have done
this
successfully, but please consider the following program:

//: C10:FriendInjection.cpp
// From Thinking in C++, 2nd Edition
// Available athttp://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
namespace Me {
class Us {
//...
public:
friend void you();
};}

// 1
void Me::you(){}

int main() {
Me::you();
return 0;

}

// 2
// defining you() after main() does not compile
// void Me::you(){}
///:~
/////////////////////////////////////////////////////////////////////////

If Me::you() is *defined* at (2) after the call in main(), gcc 4.1.1
says:
"error: 'you' is not a member of 'Me'". If, instead, Me::you is
*defined* at (1),
then it compiles.

MSVC++ Express compiles it either way without a problem, as expected.

Surely this is a bug in gcc?

What happens if you change it to

int main() {
Me::Us::you();
return 0;

I would have thought that the "Us" would be necessary.
 
H

Howard

JLS said:
What happens if you change it to

int main() {
Me::Us::you();
return 0;

I would have thought that the "Us" would be necessary.

Huh? The function you() is not a member of Us. It's a *friend* of Us.

-Howard
 
J

johnbrown105

Why?

Declaring a function as a friend does not declare or define that function.
It merely states that said function is a friend.

In case (1), you're declaring *and* defining Me::you(). If you move that to
after main() as in case (2), then when main() is compiled, Me::you() has not
yet been declared and so it's an error.

-Howard

Bruce Eckel has this to say in his book:

"Notice the two other friend functions. The first declares an ordinary
global function g( ) as a friend. But g( ) has not been previously
declared at the global scope! It turns out that friend can be used
this way to simultaneously declare the function *and* give it friend
status."

Of course, he could be wrong, but then again, so could you. I have not
read the standards myself, and I probably never will. Who agrees with
Bruce? With Howard?
 
J

James Kanze

I am doing another exercise (I repeat, *exercise*). The
(irrelevant to this discussion) point is to show that "You can
inject a friend declaration into a namespace by declaring it
within an enclosed class".

This is not up to date. Originally, a name declared in a friend
declaration was injected into the enclosing namespace (or file
scope, as it was then). This was changed in the standard, and
friend names are no longer injected.
I have done this successfully, but
please consider the following program:
//: C10:FriendInjection.cpp
// From Thinking in C++, 2nd Edition
// Available athttp://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
namespace Me {
class Us {
//...
public:
friend void you();
};}
// 1
void Me::you(){}
int main() {
Me::you();
return 0;

}

This should not compile with an up-to-date compiler. Bruce
Eckel doubtlessly learned C++ before the change:).

This would be legal according to the ARM, if you dropped the
namespace. I suspect that there were also many compilers which
supported it when they first introduced namespace as well.
// 2
// defining you() after main() does not compile
// void Me::you(){}
///:~
/////////////////////////////////////////////////////////////////////////
If Me::you() is *defined* at (2) after the call in main(), gcc 4.1.1
says:
"error: 'you' is not a member of 'Me'". If, instead, Me::you is
*defined* at (1),
then it compiles.
MSVC++ Express compiles it either way without a problem, as expected.
Surely this is a bug in gcc?

I wouldn't call it a bug. Technically, g++ is correct,
according to the standard. MSV++ is correct, according to
pre-standard conventions.

In practice, it's hard to conceive of a real program where the
change makes a difference. The most typical use of the friend
function is for operator's, which have a parameter of the class
type, and in this case, ADL now picks up the name. (ADL wasn't
present in earlier C++.) FWIW: this change broke none of my
earlier C++ (although other changes did).
 
H

Howard

As does gcc-3.4.2, OpenWatcom 1.6 and DigitalMars 8.49. For what it's
worth, the Comeau compiler (with default options) at
http://www.comeaucomputing.com/tryitout/ likes neither (1) nor (2).

So Howard, does your compiler reject (2), as you say it should?

Nope. VC++ (from Visual Studio 2003) accepts it either way. But the
question is what does the standard say, not what does Microsoft (or any
other vendor) say. I don't have the standard handy (and I have a great deal
of trouble understanding its legalese style), but I think James Kanze's
answer covers it pretty well.

-Howard
 
J

johnbrown105

This is not up to date. Originally, a name declared in a friend
declaration was injected into the enclosing namespace (or file
scope, as it was then). This was changed in the standard, and
friend names are no longer injected.

I see. Well, the book is old ((c) 2000) and the compilers mentioned in
my later post are also old (mostly).
<cut>

I wouldn't call it a bug. Technically, g++ is correct,
according to the standard. MSV++ is correct, according to
pre-standard conventions.
function is for operator's, which have a parameter of the class
type, and in this case, ADL now picks up the name. (ADL wasn't
present in earlier C++.)

Is this "ADL" why the mighty Comeau compiler accepts neither (1) nor
(2)?
 
J

James Kanze

I see. Well, the book is old ((c) 2000) and the compilers mentioned in
my later post are also old (mostly).

Exactly. In 2000, the standard was still very new, and most
compilers still implemented the pre-standard rules from the ARM.

I suspect that some compilers still implement them today, and
will continue to implement them, in order to avoid breaking
existing code. The vendors of other compilers may decide that
the amount of code actually involved is small enough to not be
worth the bother---as I said, if the function has parameters,
the compiler will find it under the new rules because of ADL.
And some vendors don't care about their customers code anyway,
and don't mind breaking it.
<cut>
The most typical use of the friend
Is this "ADL" why the mighty Comeau compiler accepts neither (1) nor
(2)?

I'd have to see the exact error message to comment. The
standard is not always very clear, but if I understand it
correctly:

namespace N {
class C
{
friend void f() ;
} ;
}

Says that there exists a function void N::f(), and that it is a
friend of N::C, but it does not make this name visible outside
of the class.

The problem is probably:

void N::f() {}

According to the standard (§8.3): "When the declarator-id is
qualified, the declaration shall refer to a previously declared
member of the class or namespace to which the qualifier
refers,[...]". This seems to be saying that the above statement
is only legal if there is already a visible declaration of
N::f(). And there isn't. But the wording is anything but
clear.

The "usual" way of doing this is something like:

namespace N {
class C
{
friend void f() ;
} ;
extern void f() ;
}

This would typically be in a header, and the definition of
N::f() would be an a source file elsewhere; where really doesn't
matter. If you do this, there should be no problems.

And ADL has nothing to do with it, of course. ADL (argument
dependent lookup) only enters into play when the function has
arguments.
 
J

johnbrown105

The problem is probably:

void N::f() {}

Comeau says:
Comeau C/C++ 4.3.9 (Mar 27 2007 17:24:47) for ONLINE_EVALUATION_BETA1
Copyright 1988-2007 Comeau Computing. All rights reserved.
MODE:strict errors C++ C++0x_extensions

"ComeauTest.c", line 15: error: namespace "Me" has no member "you"
void Me::you(){}
^

"ComeauTest.c", line 18: error: namespace "Me" has no member "you"
Me::you();
^

2 errors detected in the compilation of "ComeauTest.c".

In strict mode, with -tused, Compile failed

These messages mean what they say, if 'friend' no longer injects a
name into an enclosing namespace.

Line 15 is the definition 'void Me::you(){}' above main()
Line 18 is the call 'Me::you();' in main()

All is clear. Thank you.
 

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
473,731
Messages
2,569,432
Members
44,834
Latest member
BuyCannaLabsCBD

Latest Threads

Top