Why A Big Difference between C++98 and C++0x on a local type as atemplate-argument?

X

xmllmx

Dear all,

C++98 14.3.1 explicitly says as follows:

"A local type, a type with no linkage, an unnamed type or a type
compunded from any of these types shall not be used as a template-
argument for a template type-parameter."

And the Standard also immediately gives an example:

/*-- Source Code Begin --*/

template <class T> class X {};

void f()
{
struct S {};

X<S> x3; // error: local type used as template-argument
}

/*-- Source Code End --*/

Nevertheless, the source code should can be correctly compiled by
Visual C++. Moreover, C++0x 14.4.1 explicitly says differently as
follows:

"A template-argument for a template-parameter which is a type shall be
a type-id."

C++0x 14.4.1 also immediately gives some examples as follows:

/*-- Source Code Begin --*/

template <class T> class X {};
template <class T> void f(T t) {}
struct {} unnamed_obj;

void f()
{
struct A {};
enum { e1 };
typedef struct { } B;
B b;
X<A> x1; // OK
X<A*> x2; // OK
X<B> x3; // OK
f(e1); // OK
f(unnamed_obj); // OK
f(b); // OK
}

/*-- Source Code End --*/

Obviously, both of the most popular C++ compiler and C++0x relax the
restrictions put by C++98.

In my view, the new rules without restrictions are intuitive, easy to
use for programmers and easy to implement for compilers. What makes me
most puzzled is the hidden reason for the authors of C++98 to put such
counterintuitive restrictions on compilers and on us innocent
programmers? What's the WHY behind the C++98 restrictions?

To my experience during learning C++, C++ always trys not to put any
restrictions on programmers as much as possible except and ONLY except
the following two cases:

Case 1) One language feature/usage is dangerous and/or easy to misuse
so that some specific restrictions are necessary. e.g. restrictions on
const member functions.

case 2) One language feature/usage can not be implementd by the C++
compilers. e.g. restrictions on static_cast(staic_cast a pointer to a
virtual base to another pointer to its derived class which have at
least two immediate base classes. i.e the notorious diamond
inheritence.)

As the supreme authority in C++ world, I always regard C++98 as a
bible-like masterpiece. I don't believe the authors of C++98 had not
deeply thought the above-mentioned issue over and over before they
decided to put such restrictions into the standard. However, I indeed
could not get a convincing explanation only by myself. So I eagerly
hope someone can give me that. Any help will be highly appreciated,
many thanks in advance!
 
R

Robert Fendt

Obviously, both of the most popular C++ compiler and C++0x relax the
restrictions put by C++98.

To call VC++ the "most popular C++ compiler" is actually quite a
bold statement. That holds true only if your entire world
consists of Win32 desktop applications. Other compilers like
Comeau, Intel and GCC exist not without reason. Especially GCC,
which supports nearly any platform on the *planet*. If you look
at other domains like embedded systems, POSIX, scientific
computing etc. you will find that your "most popular" compiler
is more or less useless in every single one of them.
In my view, the new rules without restrictions are intuitive, easy to
use for programmers and easy to implement for compilers. What makes me
most puzzled is the hidden reason for the authors of C++98 to put such
counterintuitive restrictions on compilers and on us innocent
programmers? What's the WHY behind the C++98 restrictions?

You *do* know who "authors of C++98" actually are? That's right,
it's a group effort, and one of the most important parts of said
group *are* the compiler manufacturers. So technically they put
the restriction in the standard themselves.

That said, invoking templates with local types can create new
nightmares considering the proper handling of type visibility
and namespaces. Frankly, it does not seem you know too much
about compiler technicalities and especially about the headaches
templates can produce implementation-wise, so you should
perhaps be a bit more cautious about statements like "easy to
implement". To my ears, it does *not* sound exactly 'easy', and
maybe it sounded too risky at the time to dictate that all
compliant compilers had to support the feature. So maybe the
standards committee decided to play it safe; now it is clear that
the feature can be implemented in a "clean" way both concerning
implementation and semantics, so they included it in 0x. But to
speculate on events 12+ years ago is moot.

Regards,
Robert
 
X

xmllmx

To call VC++ the "most popular C++ compiler" is actually quite a
bold statement. That holds true only if your entire world
consists of Win32 desktop applications. Other compilers like
Comeau, Intel and GCC exist not without reason. Especially GCC,
which supports nearly any platform on the *planet*. If you look
at other domains like embedded systems, POSIX, scientific
computing etc. you will find that your "most popular" compiler
is more or less useless in every single one of them.


You *do* know who "authors of C++98" actually are? That's right,
it's a group effort, and one of the most important parts of said
group *are* the compiler manufacturers. So technically they put
the restriction in the standard themselves.

That said, invoking templates with local types can create new
nightmares considering the proper handling of type visibility
and namespaces. Frankly, it does not seem you know too much
about compiler technicalities and especially about the headaches
templates can produce implementation-wise, so you should
perhaps be a bit more cautious about statements like "easy to
implement". To my ears, it does *not* sound exactly 'easy', and
maybe it sounded too risky at the time to dictate that all
compliant compilers had to support the feature. So maybe the
standards committee decided to play it safe; now it is clear that
the feature can be implemented in a "clean" way both concerning
implementation and semantics, so they included it in 0x. But to
speculate on events 12+ years ago is moot.

Regards,
Robert

I'm sorry for my regarding VC++ as "the most popular C++ compiler". I
think I should rephrase it as "one of the mainstream C++ compilers".

Your explanation about this issue is acceptable.

Thank you very much.
 
B

Bo Persson

xmllmx said:
Dear all,

C++98 14.3.1 explicitly says as follows:

"A local type, a type with no linkage, an unnamed type or a type
compunded from any of these types shall not be used as a template-
argument for a template type-parameter."

And the Standard also immediately gives an example:

/*-- Source Code Begin --*/

template <class T> class X {};

void f()
{
struct S {};

X<S> x3; // error: local type used as template-argument
}

/*-- Source Code End --*/

Nevertheless, the source code should can be correctly compiled by
Visual C++. Moreover, C++0x 14.4.1 explicitly says differently as
follows:

"A template-argument for a template-parameter which is a type shall
be a type-id."

C++0x 14.4.1 also immediately gives some examples as follows:

/*-- Source Code Begin --*/

template <class T> class X {};
template <class T> void f(T t) {}
struct {} unnamed_obj;

void f()
{
struct A {};
enum { e1 };
typedef struct { } B;
B b;
X<A> x1; // OK
X<A*> x2; // OK
X<B> x3; // OK
f(e1); // OK
f(unnamed_obj); // OK
f(b); // OK
}

/*-- Source Code End --*/

Obviously, both of the most popular C++ compiler and C++0x relax the
restrictions put by C++98.

In my view, the new rules without restrictions are intuitive, easy
to use for programmers and easy to implement for compilers. What
makes me most puzzled is the hidden reason for the authors of C++98
to put such counterintuitive restrictions on compilers and on us
innocent programmers? What's the WHY behind the C++98 restrictions?

The restriction was there beacuse nobody spent enough time to figure
out what it should mean. If you have two struct S in two different
functions, will that get you one or two X<S>'s?

10 years later, the committee has figured this out , and agreed that

template <class T> class X {};

void f()
{
struct S {};

X<S> x3; // error: local type used as template-argument
}

can be compiled as

template <class T> class X {};

namespace
{
struct S {};
}

void f()
{
X<S> x3; // error: local type used as template-argument
}


which has been legal and well defined all along.



Bo Persson
 
M

Maxim Yegorushkin

The restriction was there beacuse nobody spent enough time to figure
out what it should mean. If you have two struct S in two different
functions, will that get you one or two X<S>'s?

10 years later, the committee has figured this out , and agreed that

template<class T> class X {};

void f()
{
struct S {};

X<S> x3; // error: local type used as template-argument
}

can be compiled as

template<class T> class X {};

namespace
{
struct S {};
}

void f()
{
X<S> x3; // error: local type used as template-argument
}


which has been legal and well defined all along.

What about the case when there is another function defining struct S.
They would be different types S, right? I.e. it is not quite like
putting another S in the same anonymous namespace.
 
B

Bo Persson

Maxim said:
What about the case when there is another function defining struct
S. They would be different types S, right? I.e. it is not quite like
putting another S in the same anonymous namespace.

I guess they would have to be the same S, if they are in the same
source file. If they are in different compilation units, they will get
unique names from the anonymous namespaces.

The possible non-uniqueness of the template instantiation was the
original problem.


Bo Persson
 
J

James Kanze

xmllmx wrote:

[...]
The restriction was there beacuse nobody spent enough time to figure
out what it should mean. If you have two struct S in two different
functions, will that get you one or two X<S>'s?

I don't think that there was ever any question concerning what
it should mean. Classes defined in separate functions are
distinct types, and always have been, so distinct instantiations
are needed.

There was some question concerning how this could or should be
implemented. A class defined inside a function isn't visible
outside the function, so how could a template instantiation
refer to its members.
10 years later, the committee has figured this out , and agreed that
template <class T> class X {};
void f()
{
struct S {};
X<S> x3; // error: local type used as template-argument
}
can be compiled as
template <class T> class X {};
namespace
{
struct S {};
}
void f()
{
X<S> x3; // error: local type used as template-argument
}
which has been legal and well defined all along.

It's a bit more complicated than that. What happens if you have
two functions in the same translation unit which both define a
class S?

I think that the real reason had to do with implementability.
At the time the last revision of the standard was being
finalized, most template implementations used something more or
less similar to textual replacement, and there was probably some
worry that such systems couldn't cope with elements that were
out of scope.
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top