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

Is typedef Lock<Something> Lock legal?

0 views
Skip to first unread message

apm

unread,
Feb 20, 2003, 10:12:05 PM2/20/03
to
I am trying to build a GPL'd library that compiles ok with GCC but not
with Solaris Forte 6.2. I have a feeling the code is non-std and would
appreciate any feedback.

Forte barfs when it sees a typedef of a template class where the
typedef name is the same as the template name, e.g

#include <Lock.h> // defines a template class

class Something
{
public:
typedef Lock<Something> Lock;
:
:
};

Is this legal having Lock used twice like this?

Regards,

Andrew M.

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Hyman Rosen

unread,
Feb 21, 2003, 10:48:27 AM2/21/03
to
apm wrote:
> Is this legal having Lock used twice like this?

Yes.

Terje Slettebø

unread,
Feb 21, 2003, 7:00:22 PM2/21/03
to
hyr...@mail.com (Hyman Rosen) wrote in message news:<10458379...@master.nyc.kbcfp.com>...
> apm wrote:
>
> > typedef Lock<Something> Lock;

> >
> > Is this legal having Lock used twice like this?
>
> Yes.

An odd thing, though, is that the following work:

class Test {};

Test Test; // Ok

while the following does not:

template<class T>
class Test {};

typedef Test<int> Test;

Test Test; // error: "Test" has already been declared in the current
scope

Both Intel C++ and g++ gives the same behaviour for this. Does anyone
have an explanation for this difference, perhaps with a reference to
the standard?


Regards,

Terje

tslettebo at chello no

(This thread should probably be on comp.lang.c++(.moderated), though)

Raoul Gough

unread,
Feb 26, 2003, 10:54:59 PM2/26/03
to
"apm" <ap...@student.open.ac.uk> wrote in message
news:d1a33011.03022...@posting.google.com...

> I am trying to build a GPL'd library that compiles ok with GCC but
not
> with Solaris Forte 6.2. I have a feeling the code is non-std and
would
> appreciate any feedback.
>
> Forte barfs when it sees a typedef of a template class where the
> typedef name is the same as the template name, e.g
>
> #include <Lock.h> // defines a template class
>
> class Something
> {
> public:
> typedef Lock<Something> Lock;
> :
> :
> };
>
> Is this legal having Lock used twice like this?

I don't like to disagree with Hyman Rosen, but I have my suspicions
about your example. Because it declares a type within class scope, I
would think it is illegal according to 3.3.6/1:

"2) A name N used in a class S shall refer to the same declaration in
its context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule."

Actually, now that I read that again, I'm not sure what it really
means! OTOH, I've seen g++ 3.2 barf on things like

struct X {
GUID GUID;
};

and I've heard from various sources that the above is indeed illegal.
What version of g++ was accepting the example you've got? Does forte
mind if you do the same thing outside of class scope?

Raoul Gough.

Hyman Rosen

unread,
Feb 27, 2003, 1:07:14 AM2/27/03
to
Raoul Gough wrote:
> I don't like to disagree with Hyman Rosen

Go ahead :-) Now that you pointed me at 3.3.6,
I think you're right. It claims that this
typedef int I;
class C { typedef I I; };
is an error, which can't get much plainer. I
was fooled because I tried the OP's code in
Comeau and it was accepted.

apm

unread,
Feb 27, 2003, 4:24:28 PM2/27/03
to
hyr...@mail.com (Hyman Rosen) wrote in message news:<bLg7a.10951$ES3....@nwrddc04.gnilink.net>...

> Raoul Gough wrote:
> > I don't like to disagree with Hyman Rosen
>
> Go ahead :-) Now that you pointed me at 3.3.6,
> I think you're right. It claims that this
> typedef int I;
> class C { typedef I I; };
> is an error, which can't get much plainer. I
> was fooled because I tried the OP's code in
> Comeau and it was accepted.

I cannot accept an argument that is based on whether or not a compiler
accepts it. The original code in question is accepted by GCC but not
by Forte 6.2. One of the compilers is right and the other is wrong.
But which? And why? I cannot see the answer from my reading of 3.3.6
clause 2. This is because I do not understand 3.3.6/2.

The example above is NOT of the same kind as what I posted originally.
For a start in the code above the outermost typedef is irrelevant. The
fact that my class was a template class is extremely relevant because
Foo<Bar> and Foo are different type names (or are they?).

-apm

Al Grant

unread,
Feb 27, 2003, 4:24:15 PM2/27/03
to
hyr...@mail.com (Hyman Rosen) wrote in message news:<bLg7a.10951$ES3....@nwrddc04.gnilink.net>...
> Go ahead :-) Now that you pointed me at 3.3.6,
> I think you're right. It claims that this
> typedef int I;
> class C { typedef I I; };
> is an error, which can't get much plainer. I
> was fooled because I tried the OP's code in
> Comeau and it was accepted.

I started a thread on this last year (look for "use of typedef
before redefinition in struct") when I thought g++ was wrongly
rejecting something accepted by Comeau and VC++. It turns out
that it is in error but the diagnostic is optional (so everyone
was right except me :-). I guess, since one of Comeau's strengths
is as a checkout compiler, we might see it diagnose it in future.

I suspect this is an area where the committee wanted the illegality
as a matter of principle but felt that mandating its diagnosis
would break too much existing code. I don't understand this,
if the code is worth supporting then it should be supported in the
language, if not, then forget about it. IMHO issues of syntax and
name lookup should not be implementation-dependent!

Hyman Rosen

unread,
Feb 27, 2003, 6:10:18 PM2/27/03
to
apm wrote:
> The example above is NOT of the same kind as what I posted originally.
> For a start in the code above the outermost typedef is irrelevant. The
> fact that my class was a template class is extremely relevant because
> Foo<Bar> and Foo are different type names (or are they?).

But 3.3.6/2 is not about "type names" but about "names".


A name N used in a class S shall refer to the same declaration
in its context and when re-evaluated in the completed scope of S.
No diagnostic is required for a violation of this rule.

In your code, the first use of Foo refers to the template,
but in the completed scope it refers to the typedef. Hence
you have violated the rule above. That "no diagnostic" is
pretty unfortunate.

faisal vali

unread,
Feb 28, 2003, 2:15:03 AM2/28/03
to
apm wrote:
>
> hyr...@mail.com (Hyman Rosen) wrote in message news:<bLg7a.10951$ES3....@nwrddc04.gnilink.net>...
> > Raoul Gough wrote:
> > > I don't like to disagree with Hyman Rosen
> >
> > Go ahead :-) Now that you pointed me at 3.3.6,
> > I think you're right.
<snip-snip>

> But which? And why? I cannot see the answer from my reading of 3.3.6
> clause 2. This is because I do not understand 3.3.6/2.

<snip>

Ok here's my take:

I think we all agree that 3.3.6/2 is clear that the same 'name' must
refer to the same declaration upon re-evaluation.
A name as defined by the standard is the use of an _identifier_ that
denotes an entity (or label).


class X
{
typedef Lock<Something> Lock;
};


In the typedef declaration in the above fragment we have a template-id:
Lock<Something> followed by an identifier: Lock.

This particular template-id: Lock<Something>, is composed of two names
1) Lock (lets call this one Lock@A )
2) Something

The identifier following the template-id is the name Lock (lets call
this one Lock@B).

Now 'Lock@A' is unqualified - and so when it is initially looked-up
(using unqualified name lookup) it is made to refer to the template
class Lock - since there is no member with the name of Lock or else
Lock@A would be made to refer to it (rules of lookup).
(The name 'Something' is irrelevant to our discussion - so i'll skip it)

Then Lock@B is declared (so now the name Lock refers NOT to a template
but to a type-name that is a member of the class)

Now when the definition is re-evaluated, and Lock@A undergoes
unqualified lookup - the member typename is found and Lock@A is made to
refer to the member typename and NOT the template that it initially
referred to.

Thus this is ill-formed.

The easiest fix would be to qualify the Lock template with its enclosing
namespaces.
So if it was globally defined then the following code should be
well-formed.

class X
{
typedef ::Lock<Something> Lock;
};

This is because, ::Lock is now a qualified name, and undergoes
qualified-name lookup and so will refer to the template both times.

hope that clears up things,
-Faisal Vali

Terje Slettebø

unread,
Feb 28, 2003, 2:00:22 PM2/28/03
to
alg...@myrealbox.com (Al Grant) wrote in message news:<5765b025.03022...@posting.google.com>...

> hyr...@mail.com (Hyman Rosen) wrote in message news:<bLg7a.10951$ES3....@nwrddc04.gnilink.net>...
> > Go ahead :-) Now that you pointed me at 3.3.6,
> > I think you're right. It claims that this
> > typedef int I;
> > class C { typedef I I; };
> > is an error, which can't get much plainer. I
> > was fooled because I tried the OP's code in
> > Comeau and it was accepted.
>
> I started a thread on this last year (look for "use of typedef
> before redefinition in struct") when I thought g++ was wrongly
> rejecting something accepted by Comeau and VC++. It turns out
> that it is in error but the diagnostic is optional (so everyone
> was right except me :-). I guess, since one of Comeau's strengths
> is as a checkout compiler, we might see it diagnose it in future.
>
> I suspect this is an area where the committee wanted the illegality
> as a matter of principle but felt that mandating its diagnosis
> would break too much existing code.

I don't think so. The code is ill-formed, whether or not diagnostics
is output. This means, in essense, undefined behaviour. So just
because a compiler doesn't diagnose it, doesn't mean it will work.
It's the same with e.g. violation of ODR.

Instead, "no diagnostics is required" is typically used if it would be
very difficult for an implementation to detect the violation. As at
least g++ detects this, and it should be quite easy, that doesn't seem
to be the case here.

However, when reading 1.3.14, "well-formed program", it says:
"a C++ program constructed according to the syntax rules, diagnosable
semantic rules, and the One Definition Rule (3.2)."

Does this really mean what you say here, that if it's not diagnosable
(as in no diagnostics required), it's well-formed?


Regards,

Terje

Raoul Gough

unread,
Feb 28, 2003, 2:00:22 PM2/28/03
to
"Al Grant" <alg...@myrealbox.com> wrote in message
news:5765b025.03022...@posting.google.com...

> hyr...@mail.com (Hyman Rosen) wrote in message
news:<bLg7a.10951$ES3....@nwrddc04.gnilink.net>...
> > Go ahead :-) Now that you pointed me at 3.3.6,
> > I think you're right. It claims that this
> > typedef int I;
> > class C { typedef I I; };
> > is an error, which can't get much plainer. I
> > was fooled because I tried the OP's code in
> > Comeau and it was accepted.
>
> I started a thread on this last year (look for "use of typedef
> before redefinition in struct") when I thought g++ was wrongly
> rejecting something accepted by Comeau and VC++. It turns out
> that it is in error but the diagnostic is optional (so everyone
> was right except me :-). I guess, since one of Comeau's strengths
> is as a checkout compiler, we might see it diagnose it in future.
>
> I suspect this is an area where the committee wanted the illegality
> as a matter of principle but felt that mandating its diagnosis
> would break too much existing code. I don't understand this,
> if the code is worth supporting then it should be supported in the
> language, if not, then forget about it. IMHO issues of syntax and
> name lookup should not be implementation-dependent!

Actually, the rule does break existing code, because the standard
explicitly allows the implementation to do anything at all with
violations of "no diagnostic" rules (see 1.4/2). I assume the "no
diagnostic" is just to simplify the compiler writers' job (and
unfortunately makes programmers' harder).

Raoul Gough.

Al Grant

unread,
Mar 3, 2003, 7:56:01 PM3/3/03
to
Raoul...@yahoo.co.uk ("Raoul Gough") wrote in message news:<b3nfl4$1obkug$1...@ID-136218.news.dfncis.de>...

> "Al Grant" <alg...@myrealbox.com> wrote in message
> news:5765b025.03022...@posting.google.com...
> > I suspect this is an area where the committee wanted the illegality
> > as a matter of principle but felt that mandating its diagnosis
> > would break too much existing code.
>
> Actually, the rule does break existing code, because the standard
> explicitly allows the implementation to do anything at all with
> violations of "no diagnostic" rules (see 1.4/2).

It only breaks existing code if compiled under such an implementation.
The point is that it allows other implementations to accept the code
while still being Standard C++ compilers.

Gabriel Dos Reis

unread,
Mar 4, 2003, 11:45:19 AM3/4/03
to
alg...@myrealbox.com (Al Grant) writes:

| Raoul...@yahoo.co.uk ("Raoul Gough") wrote in message news:<b3nfl4$1obkug$1...@ID-136218.news.dfncis.de>...
| > "Al Grant" <alg...@myrealbox.com> wrote in message
| > news:5765b025.03022...@posting.google.com...
| > > I suspect this is an area where the committee wanted the illegality
| > > as a matter of principle but felt that mandating its diagnosis
| > > would break too much existing code.
| >
| > Actually, the rule does break existing code, because the standard
| > explicitly allows the implementation to do anything at all with
| > violations of "no diagnostic" rules (see 1.4/2).
|
| It only breaks existing code if compiled under such an implementation.
| The point is that it allows other implementations to accept the code
| while still being Standard C++ compilers.

Yes, they may not emit any diagnostic but they may not produce a
meaningful executable.

--
Gabriel Dos Reis, g...@integrable-solutions.net

Raoul Gough

unread,
Mar 4, 2003, 2:23:37 PM3/4/03
to
"Al Grant" <alg...@myrealbox.com> wrote in message
news:5765b025.03030...@posting.google.com...

> Raoul...@yahoo.co.uk ("Raoul Gough") wrote in message
news:<b3nfl4$1obkug$1...@ID-136218.news.dfncis.de>...
[discussion of 3.3.6 vs. name redefinition within class]

> > Actually, the rule does break existing code, because the standard
> > explicitly allows the implementation to do anything at all with
> > violations of "no diagnostic" rules (see 1.4/2).
>
> It only breaks existing code if compiled under such an
implementation.
> The point is that it allows other implementations to accept the code
> while still being Standard C++ compilers.

AFAIK, the implementation is free to build an executable even when
diagnostics are required by the standard (for instance, g++ is pretty
lenient on broken for-loop scoping, although it produces useful
warnings). From that point of view, I don't see that making the
diagnostic optional has much to do with backwards compatibility.

The worrying question for me is what do the non-diagnosing
implementations *do* with violations of 3.3.6/1? The standard doesn't
try to define the behaviour, so you could inadvertently be on very
unreliable ground:

typedef int Type;

struct A {
Type mT1;
typedef double Type;
inline Type foo () { return mT1 / 2; }
};

g++ rejects the above code with an informative error message:

name_redef.cc:5: declaration of `typedef double A::Type'
name_redef.cc:1: changes meaning of `Type' from `typedef int Type'

Raoul Gough.

Al Grant

unread,
Mar 12, 2003, 6:00:27 PM3/12/03
to
Raoul...@yahoo.co.uk ("Raoul Gough") wrote in message news:<b429n7$1q6vtf$1...@ID-136218.news.dfncis.de>...

> The worrying question for me is what do the non-diagnosing
> implementations *do* with violations of 3.3.6/1? The standard doesn't
> try to define the behaviour, so you could inadvertently be on very
> unreliable ground:

On the other hand you could be on reliable ground; the rule is
unnecessarily (or at least unintuitively) strict.

> typedef int Type;
>
> struct A {
> Type mT1;
> typedef double Type;
> inline Type foo () { return mT1 / 2; }
> };
>
> g++ rejects the above code with an informative error message:
>
> name_redef.cc:5: declaration of `typedef double A::Type'
> name_redef.cc:1: changes meaning of `Type' from `typedef int Type'

Replace the typedef in the struct with 'typedef int Type'.
Now, is the error message informative? Should this be an error?

Another point is that AFAICS this is legal C:
enum { N = 1 };
struct S { int N[N]; };
But 3.3.6#2 appears to make this dangerous when compiled as C++.
Another quiet change?

Raoul Gough

unread,
Mar 17, 2003, 8:03:27 PM3/17/03
to
"Al Grant" <alg...@myrealbox.com> wrote in message
news:5765b025.0303...@posting.google.com...

> Raoul...@yahoo.co.uk ("Raoul Gough") wrote in message
news:<b429n7$1q6vtf$1...@ID-136218.news.dfncis.de>...
> > The worrying question for me is what do the non-diagnosing
> > implementations *do* with violations of 3.3.6/1? The standard
doesn't
> > try to define the behaviour, so you could inadvertently be on very
> > unreliable ground:
>
> On the other hand you could be on reliable ground; the rule is
> unnecessarily (or at least unintuitively) strict.

I would say that the rule is reasonably strict for the programmer, but
undesirably lenient on the compiler. I'm biased, of course, since I'm
just a programmer and don't know how hard diagnosing the rule would
be.

>
> > typedef int Type;
> >
> > struct A {
> > Type mT1;
> > typedef double Type;
> > inline Type foo () { return mT1 / 2; }
> > };
> >
> > g++ rejects the above code with an informative error message:
> >
> > name_redef.cc:5: declaration of `typedef double A::Type'
> > name_redef.cc:1: changes meaning of `Type' from `typedef int Type'
>
> Replace the typedef in the struct with 'typedef int Type'.
> Now, is the error message informative? Should this be an error?

So the error message would be harder to understand, but I think it
should still be an error. The standard would presumably still classify
it as ill-formed, since in one case "Type" would mean ::Type and in
the other A::Type (both int typedefs, but still different
declarations).

On a related issue, I remember when I first got into C++, I found it
weird that code like the following compiles:

struct A {
int get_member() const { return m; }
private:
int m;
};

Where the usage of the member variable lexically preceeds its
declaration. That's why I think the rules in 3.3.6 should require
diagnosis, since some bozo could inadvertently introduce a different
"m" object into the global or namespace scope before the class
definition, completely altering the likely interpretation of the code.
That makes it pretty dangerous, I think.

>
> Another point is that AFAICS this is legal C:
> enum { N = 1 };
> struct S { int N[N]; };
> But 3.3.6#2 appears to make this dangerous when compiled as C++.
> Another quiet change?

I guess there is no potential for problems in C, since you can't
introduce any code (or even typedefs?) into the struct scope, where
confusion could arise.

--
Raoul Gough
see http://home.clara.net/raoulgough/ for my work availability

0 new messages