473,396 Members | 1,917 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Forward declare a templatized class

I know this has been asked before, but I just can't find the answer in
the sea of hits...

How do you forward declare a class that is *not* paramaterized, but is
based on a template class?

Here's what I thought should work, but apparently doesn't:

class Foo;
void f1(Foo* p)
{
}

template<class T>
class FooBase
{
};

typedef FooBase<int> Foo;

This barks that Foo is redclared with different basic types. What am I
missing?

Jan 29 '06 #1
23 3800
ma********@notlimited.com wrote:
I know this has been asked before, but I just can't find the answer in
the sea of hits...

How do you forward declare a class that is *not* paramaterized, but is
based on a template class?

Here's what I thought should work, but apparently doesn't:

class Foo;
void f1(Foo* p)
{
}

template<class T>
class FooBase
{
};

typedef FooBase<int> Foo;

This barks that Foo is redclared with different basic types. What am I
missing?


The following works:

template<class T> class FooBase;
typedef FooBase<int> Foo;

void f1(Foo* p)
{
}

template<class T>
class FooBase
{
};

I'm not sure if there's a less clumsy way of doing it; one that doesn't
require two lines to forward declare.

--
Ben Radford
"Why is it drug addicts and computer aficionados are both called users?"
Jan 29 '06 #2
Ben, I appreciate you're taking the time to reply to my post, but it
looks like I wasn't clear enough. The code I included was meant to be
a condensed example of a larer code base.

Let me be more clear.

If the first four lines are in one header file (imagine
proj_utility.h), and the remaining lines are in another header file
(say foo.h), then imagine a third file that includes both headers. The
sequence of statements the compiler will process as it works through
the compilation unit will look like what I originally posted.

Your solution assumes I know what Foo is implemented in terms of which
is exactly what I'm trying to remove from F1()'s header file. Worse,
if I did go ahead and do this, then if the declaration of Foo ever
changed in foo.h, I'd have to go back to proj_util.h and make the same
change. I hope you see how gross that is.

So, anybody know how to forward declare a class based on a templatized
class like I've illustrated?

Jan 29 '06 #3
ma********@notlimited.com wrote:
So, anybody know how to forward declare a class based on a templatized
class like I've illustrated?


Yes. Unfortunately, that's not what you've illustrated. A "typedef"
directive does not create a new type, it creates an alias. There is no
forward-declaration of such aliases. Which is no surprise -- for
example, we can't forward-declare namespaces, either.

If you want to accomplish this effect by creating a new type
functionally identical with, but distinct from the associated template
instantiation, you should be able to get what you want through trivial
public inheritance. For example:

class IntVec3d;
void f(IntVec3d v);
template <typename T> class Vec3d { /*...*/ };
struct IntVec3d : Vec3d<int> { /* EMPTY */ };

Does that satisfy?

Luke

Jan 29 '06 #4
Luke, definitely interesting. And, I do understand your point. The
typedef doesn't introduce a new type.

Your solution implies I have control over the definition of Foo (or
IntVec3d in your example). This isn't the case for my design. If you
imagine "Foo" being "std::string", you might be able to appreciate the
issue.

With that said, your post did get me to think about the solution:

//header1.h
// Forward declare Foo
template<class T> class FooBase;
typedef FooBase<int> Foo;
// Refer to Foo without using it directly
void f1(Foo* p)
{
}

//header2.h
// Actual declaration of Foo (needed to use Foo)
template<class T>
class FooBase
{
};
typedef FooBase<int> Foo;

This works, and decouples the two headers. But, this requires exposing
a lot more knowledge than it should. Normally for types that are
referenced, but not used, all you need to know (or expose) is its name
(e.g. class Fee).

However, if a type is ilemented in terms of an underlying template
(definitely an irrelevant implementation detail), you need to expose
the class' name, the name of its template along with the template's
signature (number of parameters), and even worse, the particular
instantiation of the underlying template. That's a dramatically
increased surface area for no useful reason.

At least I've got a work around now, so thanks Luke! Is there a better
way to decouple the two interfaces?

Jan 29 '06 #5
Ben, after re-reading your post, I realized you hadt exactly right.
Sorry for misunderstanding it at first. -MM

Jan 29 '06 #6
ma********@notlimited.com wrote:
Luke, definitely interesting. And, I do understand your point. The
typedef doesn't introduce a new type.
Exactly. Yet you seem to still be clinging to the notion that somehow
the typedef is a "class implemented in terms of" the template
instantiation it is an alias for -- this is not the case.
Your solution implies I have control over the definition of Foo (or
IntVec3d in your example). This isn't the case for my design. If you
imagine "Foo" being "std::string", you might be able to appreciate the
issue.
std::string, you should be aware, *is* a typedef. And you can't
forward-declare it. You can, if you really want, forward-declare the
basic_string typedef and repeat the library's typedef verbatim if you
want the name available without having to #include <string>, but
usually people just #include <string>.
With that said, your post did get me to think about the solution:
That's the same as the first solution you were given, I believe.
This works, and decouples the two headers. But, this requires exposing
a lot more knowledge than it should. Normally for types that are
referenced, but not used, all you need to know (or expose) is its name
(e.g. class Fee).
Uh-huh. And the name of the type named Vec3d<int> is "Vec3d<int>."
You want to use that without naming it, apparently. No can do.
However, if a type is ilemented in terms of an underlying template
(definitely an irrelevant implementation detail), you need to expose
the class' name, the name of its template along with the template's
signature (number of parameters), and even worse, the particular
instantiation of the underlying template. That's a dramatically
increased surface area for no useful reason.
No, you're still thinking that a typedef creates a type. You can't
forward-declare typedefs; there are some subtleties as to why, but
you've never been able to and it doesn't matter whether the object of
the typedef is a built-in type, a regular class, or a template
instantiation. The typedef is not "a type implemented in terms of an
underlying template," it is an *alias* for a particular template
instantiation. typedefs have no privacy. You can't get coupling so
loose that all you have is an alias for the type you're using.

The point is, you can either create a new type or not, and each
possibility has things that it does and does not permit you to do. You
can't mix them in the same case, though. Pick one, understand it, and
do it.
At least I've got a work around now, so thanks Luke! Is there a better
way to decouple the two interfaces?


Yes; see my original post and think about why it would work just fine
that way. You get the same functionality, and you don't need to
control the definition of the template used. The only wrinkle is that,
by defining a new type, you may have to deal with conversions or
something in case you have to interoperate with instances of the
original template (not your derived type). That's manageable, though,
and may not even be relevant in your case.

Luke

Jan 29 '06 #7
Luke Meyers wrote:
std::string, you should be aware, *is* a typedef. [Yes.] And you can't
forward-declare it. [Unless] you really want, forward-declare the
basic_string typedef and repeat the library's typedef verbatim if you
want the name available without having to #include <string>, but
usually people just #include <string>.


That people just give up and "#include <string>" speaks exactly to the
problem I was hoping had a solution. If the designer's intention is to
say their interface (header) depends on the specifics of std::string,
then this is exactly what they should do; #include <string>.

OTOH, if they really only want to have their interface refer to
std::strings, or any other class, (i.e. through pointers and/or
references), the LAST thing they should do is #include which increases
coupling unnecessarily. They *should* go to the trouble of forward
declaring the std::string type identifier as you illustrated. (I get
your point that it's an alias, and not a class proper.)

With that said, your post did get me to think about the solution:

That's the same as the first solution you were given, I believe.


Yes. That's why I posted the apology to Ben. I missed it at first.

This works, and decouples the two headers. But, this requires exposing
a lot more knowledge than it should. Normally for types that are
referenced, but not used, all you need to know (or expose) is its name
(e.g. class Fee).

Uh-huh. And the name of the type named Vec3d<int> is "Vec3d<int>."
You want to use that without naming it, apparently. No can do.


No. I want to use the type identifier IntVec3d (in your example)
without needing to know - or to depend on - how it is particularly
declared.

The whole point of using typedef is to abstract away the details of the
underlying declaration allowing the designer increased flexibility to
change the particular implementation (e.g. to refactor the design).

But, since the language is what it is, it doesn't make sense to rail
against it here. Thanks to you and Ben, I understand this issue much
better now.

On the other hand, the upshot of your very salient point that
typedef's are not classes is that robust public interfaces should
never export "class like" type identifiers that are typedefs of
templated classes. They should always use the mechanism you
illustrated to make template based type identifiers more classy. ;o)

Concretely, the STL string identifier should be declared "struct
string : public basic_string<char> {};" instead of "typedef
basic_string<char> string;" so it can be reasonably forward declared.
Right?

As I'm sure you're aware, the empty curly braces are not nearly
empty in actual practice. Since the new class masks any constructors
in the base class with its default constructor, all of the constructors
in the base class will need to be hoisted up a level. (There a number
of them in basic_string<char>.)

-MM

Jan 29 '06 #8
ma********@notlimited.com wrote:
Luke Meyers wrote:
std::string, you should be aware, *is* a typedef. [Yes.] And you can't
forward-declare it. [Unless] you really want, forward-declare the
basic_string typedef and repeat the library's typedef verbatim if you
want the name available without having to #include <string>, but
usually people just #include <string>.
That people just give up and "#include <string>" speaks exactly to the
problem I was hoping had a solution. If the designer's intention is to
say their interface (header) depends on the specifics of std::string,
then this is exactly what they should do; #include <string>.


A <stringfwd> header could always be added, so that the appropriate
forward declaration can be done in just one place, and referenced from
many. This is the pattern of the <iosfwd> standard library header --
google it if you're not familiar.
OTOH, if they really only want to have their interface refer to
std::strings, or any other class, (i.e. through pointers and/or
references), the LAST thing they should do is #include which increases
coupling unnecessarily. They *should* go to the trouble of forward
declaring the std::string type identifier as you illustrated. (I get
your point that it's an alias, and not a class proper.)
A <stringfwd> header (or the like) would address this.
No. I want to use the type identifier IntVec3d (in your example)
without needing to know - or to depend on - how it is particularly
declared.

The whole point of using typedef is to abstract away the details of the
underlying declaration allowing the designer increased flexibility to
change the particular implementation (e.g. to refactor the design).

But, since the language is what it is, it doesn't make sense to rail
against it here. Thanks to you and Ben, I understand this issue much
better now.
No, but it's usually worthwhile to ponder how a particular wrinkle of
the language pertains to and is (or is not) consistent with the overall
philosophy of the language. Consider it from the following
perspective. A typedef exists in a similar role to a
forward-declaration; it is a mechanism for talking about the existence
of a type. Both might be considered "first order" type-related
entities, since they deal with types directly. A forward-declaration
*of* a typedef is more indirect by one degree, and introduces a new
category, "second order" type-related entities. Such indirection can
increase indefinitely, so a language designer must decide on a
reasonable compromise between generality and practicality. In C++, the
line is drawn at the first order.

Incidentally, none of the above language is in any way
"official"/"canonical" jargon -- it's just what I made up on the spot
to express my point. ;)
On the other hand, the upshot of your very salient point that
typedef's are not classes is that robust public interfaces should
never export "class like" type identifiers that are typedefs of
templated classes. They should always use the mechanism you
illustrated to make template based type identifiers more classy. ;o)
I wouldn't say that. I don't see in what way class template
instantiations are different from regular classes in this context, so
I'm a bit baffled as to why you've singled them out. And introducing a
new "wrapper" type for every template instantiation would just defeat
the whole purpose of templates.
Concretely, the STL string identifier should be declared "struct
string : public basic_string<char> {};" instead of "typedef
basic_string<char> string;" so it can be reasonably forward declared.
Right?
No, because then you lose the ability to interoperate std::string with
std::basic_string<char> (incidentally, there are in actuality three
template parameters), without introducing additional conversion logic.
Useful sometimes perhaps, but not a universal solution by any means.
As I'm sure you're aware, the empty curly braces are not nearly
empty in actual practice. Since the new class masks any constructors
in the base class with its default constructor, all of the constructors
in the base class will need to be hoisted up a level. (There a number
of them in basic_string<char>.)


Another very good reason not to use inheritance willy-nilly. It's good
when it's good, and in all other cases it's not good.

Luke

Jan 30 '06 #9
Luke Meyers wrote:
The whole point of using typedef is to abstract away the details of the
underlying declaration allowing the designer increased flexibility to
change the particular implementation (e.g. to refactor the design).
Wondering why you didn't have anything to say here. Assuming it's
because you agreed... :)

But, since the language is what it is, it doesn't make sense to rail
against it here. Thanks to you and Ben, I understand this issue much
better now.
No, but it's usually worthwhile to ponder how a particular wrinkle of
the language pertains to and is (or is not) consistent with the overall
philosophy of the language. Consider it from the following
perspective. A typedef exists in a similar role to a
forward-declaration; it is a mechanism for talking about the existence
of a type. Both might be considered "first order" type-related
entities, since they deal with types directly. A forward-declaration
*of* a typedef is more indirect by one degree, and introduces a new
category, "second order" type-related entities. Such indirection can
increase indefinitely, so a language designer must decide on a
reasonable compromise between generality and practicality. In C++, the
line is drawn at the first order.

Incidentally, none of the above language is in any way
"official"/"canonical" jargon -- it's just what I made up on the spot
to express my point. ;)


Although I'm clear about the "order" distinction you're drawing
here, I have to admit I don't understand the utility. For a compiler
writer, "class Foo;", "typedef Foo Bar;", and "template<class
T> Fee;" each introduce a single new identifier into the type
namespace. The compiler doesn't care how many names have been
introduced before. Right? So, there's no real distinction between
the 1st order, and the nth order.

On the other hand, the upshot of your very salient point that
typedef's are not classes is that robust public interfaces should
never export "class like" type identifiers that are typedefs of
templated classes. They should always use the mechanism you
illustrated to make template based type identifiers more classy. ;o)

I wouldn't say that. I don't see in what way class template
instantiations are different from regular classes in this context, so
I'm a bit baffled as to why you've singled them out. And introducing a
new "wrapper" type for every template instantiation would just defeat
the whole purpose of templates.


The reason they are different is because you cannot forward declare a
type identifier that's based on a templated type without introducing
the name and signature of the underlying template. That's badness
and reduces the ability of the interface designer to refactor or modify
the underlying implementation in the future.

Do you see how they are different now?

To be clear, I'm not arguing to wrap every template declaration
(which does defeat the purpose). And I'm not arguing to wrap every
possible template instantiation (which would be impossible).

I am arguing any "class like" type identifiers that are exported
from a public interface should be wrapped (if it's not a class
already). This would definitely include any identifiers that export
specific template specializations (e.g. std::string, or IntVec3d). If
these specializations are what you mean by "instantiation", then
yes, every exported instantiation should be wrapped.

Concretely, the STL string identifier should be declared "struct
string : public basic_string<char> {};" instead of "typedef
basic_string<char> string;" so it can be reasonably forward declared.
Right?

No, because then you lose the ability to interoperate std::string with
std::basic_string<char> (incidentally, there are in actuality three
template parameters)[1], without introducing additional conversion logic.
Useful sometimes perhaps, but not a universal solution by any means.

As I'm sure you're aware, the empty curly braces are not nearly
empty in actual practice. Since the new class masks any constructors
in the base class with its default constructor, all of the constructors
in the base class will need to be hoisted up a level. (There a number
of them in basic_string<char>.)


Not true (to your lack of interoperability assertion). As I mention in
the subsequent paragraph, your solution does need to include
declarations for any constructors in the underlying class.
Incidentally, it will also need declarations of any explicit assignment
operators in the underlying class (if there are any) since the default
assignment operator will mask those as well.

If you do the required work, then "typedef basic_string<char>
string;" declaration will be indistinguishable[2] from the "struct
string : basic_char<char> {...};" form except you can then forward
declare with "class string;".

Here's some working code. Try it.

namespace std
{
class better_string : public basic_string<char>
{
public:
better_string(const allocator_type& a = allocator_type()) :
basic_string<value_type>(a) {}
better_string(const basic_string<value_type>& str,
size_type pos = 0, size_type n = npos, const allocator_type& a =
allocator_type()) :
basic_string<value_type>(str, pos, n, a) {}
better_string(const value_type* s, size_type n, const
allocator_type& a = allocator_type()) :
basic_string<value_type>(s, n, a) {}
better_string(const value_type* s, const allocator_type& a
= allocator_type()) :
basic_string<value_type>(s, a) {}
better_string(size_type n, value_type c, const
allocator_type& a = allocator_type()) :
basic_string<value_type>(n, c, a) {}
better_string(const_iterator begin, const_iterator end,
const allocator_type& a = allocator_type()) :
basic_string<value_type>(begin, end, a) {}

better_string& operator=(const basic_string<value_type>&
str)
{ basic_string<value_type>::operator=(str); return *this;
}
better_string& operator=(const value_type* str)
{ basic_string<value_type>::operator=(str); return *this;
}
better_string& operator=(value_type c)
{ basic_string<value_type>::operator=(c); return *this; }
};
}

Like I said, the curly braces are not quite empty (and won't be for
most non-trivial classes).

This covers all of the documented call signatures according to
Addison-Wesley's "STL Tutorial and Reference Guide, Second
Edition." I don't have a copy of the ANSI/ISO STL document, but
the construct is easy to extend/modify if I've missed anything.

Here's some code that exercises better_string a little bit. Note
it's completely interoperable with std::string as well as classic C
strings (char*).

void better_test()
{
string s1("some text");
better_string bs1(s1);
assert(bs1 == s1);
assert(bs1 == "some text");

bs1 = 'X';
assert(bs1 == "X");
bs1 = "test";
assert(bs1 == "test");
bs1 = s1;
assert(bs1 == "some text");
}

Another very good reason not to use inheritance willy-nilly. It's good
when it's good, and in all other cases it's not good.


Luke, this is hardly willy-nilly. This is to decrease the coupling
between a library and its clients which is always good.

I'm having a hard time imagining a public interface where it would be
good to export an identifier with the "typedef Bar<Foo> FooBar;"
syntax. To be clear, "good" means better in any sense than
exporting the same identifier with your "struct FooBar: Bar<Foo>
{...};" syntax.

[1] Yes, I'm aware there are three parameters in the basic_string
template definition. As you're aware, the second and third
parameters have defaults, so basic_string<char> is *identical* to
basic_string<char, char_traits<char>, allocator<char> > just a lot
shorter (and a lot clearer about the intent, IMHO).

[2] What I mean by "indistinguishable" is that any code that
compiled when std::string was defined as "typedef basic_string<char>
string;" will compile with the new declaration. Except of course the
gross forward declaration currently required, but that's exactly what
I'm hoping to get rid of. More importantly, the compiled code will
behave the same as the code compiled under the inferior declaration.

Jan 30 '06 #10
ma********@notlimited.com wrote:
Luke Meyers wrote:
Although I'm clear about the "order" distinction you're drawing
here, I have to admit I don't understand the utility. For a compiler
writer, "class Foo;", "typedef Foo Bar;", and "template<class
T> Fee;" each introduce a single new identifier into the type
namespace. The compiler doesn't care how many names have been
introduced before. Right? So, there's no real distinction between
the 1st order, and the nth order.
Consider some practical effects. The availability, or lack thereof, of
the aliased type would interfere with the type system in various ways.
Consider overloading, for one, or simple assignment. If I see a
typedef (as they actually exist), I know what actual type I'm dealing
with. If I encounter a situation where the actual type would be a
match (say, resolving an overloaded function), I can use that
information. Were I then to rearrange my code to have just a
(notional) "typedef forward-declaration," the semantics would change.
The forward-declared typedef, lacking information of the type it
represents, would not resolve correctly for function overloads as
before, or for template specializations, and so on. So you've either
got to allow this anyway, which would mean that a typedef really
creates a new type (not gonna happen), or disallow it. Disallowing it
means having a higher-order entity for the compiler to manage. In
addition to knowing about a class and tracking whether the definition
has been seen or not, it would now have to track additional information
about whether the *typedef's* definition has been seen or not. Imagine
the growing complexity when you consider "chaining" of typedefs --
defining them in terms of one another. Do you see how this is a
fundamental increase in compiler complexity, for questionable benefit?
I wouldn't say that. I don't see in what way class template
instantiations are different from regular classes in this context, so
I'm a bit baffled as to why you've singled them out. And introducing a
new "wrapper" type for every template instantiation would just defeat
the whole purpose of templates.


The reason they are different is because you cannot forward declare a
type identifier that's based on a templated type without introducing
the name and signature of the underlying template. That's badness
and reduces the ability of the interface designer to refactor or modify
the underlying implementation in the future.


Buh... grrr. argghhh..... okay, listen. You can't forward declare ANY
"type identifier" (read: typedef) without introducing the type for
which it is an alias. For regular classes, that's just the class name.
For class template instantiations, in effect, the class name is just
"longer and has some funny brackets in it and stuff." A class template
instantiation *is* a class. They're the same, stop treating them
differently!
Do you see how they are different now?
Do you see how they're the same?
I am arguing any "class like" type identifiers that are exported
from a public interface should be wrapped (if it's not a class
already).
How do you know that's the right criterion, to the extent that you can
universally qualify it like that?
This would definitely include any identifiers that export
specific template specializations (e.g. std::string, or IntVec3d).
Look up "export" and "template specialization" in a trusted C++
reference, please, to see how some might read this as confusing.
If
these specializations are what you mean by "instantiation", then
yes, every exported instantiation should be wrapped.
Look up "instantiation," too. We have to be working with the same
vocabulary, and the standard provides that.
Not true (to your lack of interoperability assertion). As I mention in
the subsequent paragraph, your solution does need to include
declarations for any constructors in the underlying class.
Incidentally, it will also need declarations of any explicit assignment
operators in the underlying class (if there are any) since the default
assignment operator will mask those as well.

If you do the required work, then "typedef basic_string<char>
string;" declaration will be indistinguishable[2] from the "struct
string : basic_char<char> {...};" form except you can then forward
declare with "class string;".
That is naive and incorrect. They are different types, and a bunch of
forwarding functions isn't going to make them the same. The ways in
which they differ are mainifold. Consider:
(1) Does sizeof(better_string) == sizeof(basic_string<char>)?
(2) Is std::vector<better_string> the same type as std::vector<
basic_string<char> >? Can they interoperate?
(3) What about cases where function overloading treats an exact match
differently from implicit convertibility?
(4) Can a better_string* point to a basic_string<char>? What about
arrays?
(5) What if the base type does not define a virtual destructor?
Here's some working code. Try it.
[snip fugly cruft]
Like I said, the curly braces are not quite empty (and won't be for
most non-trivial classes).
That's a damn lot of boilerplate to maintain.
Another very good reason not to use inheritance willy-nilly. It's good
when it's good, and in all other cases it's not good.


Luke, this is hardly willy-nilly. This is to decrease the coupling
between a library and its clients which is always good.


There you go with "always" again. Loose coupling is a principle of
sound design, not an end in itself. There are other concerns against
which it must be balanced.
I'm having a hard time imagining a public interface where it would be
good to export an identifier with the "typedef Bar<Foo> FooBar;"
syntax. To be clear, "good" means better in any sense than
exporting the same identifier with your "struct FooBar: Bar<Foo>
{...};" syntax.
Anywhere that you didn't want to complicate your life by introducing a
multiplicity of incompatible types in all your interfaces.
[2] What I mean by "indistinguishable" is that any code that
compiled when std::string was defined as "typedef basic_string<char>
string;" will compile with the new declaration. Except of course the
gross forward declaration currently required, but that's exactly what
I'm hoping to get rid of. More importantly, the compiled code will
behave the same as the code compiled under the inferior declaration.


Right. Well, have another look. And think carefully about how
presumptuous language like "inferior" sounds, when what you're
referring to is the ISO standard vs. your own half-baked idea.

Luke

Jan 30 '06 #11
ma********@notlimited.com wrote:
namespace std
{
class better_string : public basic_string<char>
{

[snip]

As far as I know, you are not allowed to add names to the std namespace.

--
Marcus Kwok
Jan 30 '06 #12
Marcus Kwok wrote:
ma********@notlimited.com wrote:
namespace std
{
class better_string : public basic_string<char>
{

[snip]

As far as I know, you are not allowed to add names to the std namespace.


Why on earth not? Namespaces are open. Is ::std distinguished in some
way by the standard to explicitly prevent this? There are legitimate
reasons to add things to ::std.

Luke

Jan 30 '06 #13
Luke Meyers <n.***********@gmail.com> wrote:
Marcus Kwok wrote:
ma********@notlimited.com wrote:
> namespace std
> {
> class better_string : public basic_string<char>
> {

[snip]

As far as I know, you are not allowed to add names to the std namespace.


Why on earth not? Namespaces are open. Is ::std distinguished in some
way by the standard to explicitly prevent this? There are legitimate
reasons to add things to ::std.


I do not have a copy of the standard, but I did find this thread on
comp.lang.c++.moderated:

http://groups.google.com/group/comp....00ffda21bb3bb4

From 17.4.3.1/1:

It is undefined for a C++ program to add declarations or definitions
to namespace std or namespaces within namespace std unless otherwise
specified. A program may add template specializations for any
standard library template to namespace std. Such a specialization
(complete or partial) of a standard library template results in
undefined behavior unless the declaration depends on a user-defined
name of external linkage and unless the specialization meets the
standard library requirements for the original template.

--
Marcus Kwok
Jan 30 '06 #14
Luke Meyers wrote:
Marcus Kwok wrote:
ma********@notlimited.com wrote:
> namespace std
> {
> class better_string : public basic_string<char>
> {

[snip]

As far as I know, you are not allowed to add names to the std namespace.


Why on earth not? Namespaces are open. Is ::std distinguished in some
way by the standard to explicitly prevent this? There are legitimate
reasons to add things to ::std.


Maybe there are reasons, but the standard is quite explicit.

Clause 17.4.3.1/1:

It is undefined for a C + + program to add declarations or definitions to
namespace std or namespaces within namespace std unless otherwise
specified. A program may add template specializations for any standard
library template to namespace std. Such a specialization (complete or
partial) of a standard library template results in undefined behavior
unless the declaration depends on a user-defined name of external linkage
and unless the specialization meets the standard library requirements for
the original template.
Best

Kai-Uwe Bux

Jan 30 '06 #15
Kai-Uwe Bux wrote:
Luke Meyers wrote:
Marcus Kwok wrote:
As far as I know, you are not allowed to add names to the std namespace.


Why on earth not? Namespaces are open. Is ::std distinguished in some
way by the standard to explicitly prevent this? There are legitimate
reasons to add things to ::std.


Maybe there are reasons, but the standard is quite explicit.

Clause 17.4.3.1/1:

It is undefined for a C + + program to add declarations or definitions to
namespace std or namespaces within namespace std unless otherwise
specified. A program may add template specializations for any standard
library template to namespace std. Such a specialization (complete or
partial) of a standard library template results in undefined behavior
unless the declaration depends on a user-defined name of external linkage
and unless the specialization meets the standard library requirements for
the original template.


Ah, and it's exactly template specializations that I had in mind. But
consider another case -- what if I wanted to define an operator<< for
vector? I ran into this a while back, and due to the way ADL/Koenig
lookup works, and the interface principle, it seems you'd want to put
that in ::std.

Luke

Jan 31 '06 #16
Luke Meyers wrote:
Kai-Uwe Bux wrote:

[snip]
Clause 17.4.3.1/1:

It is undefined for a C + + program to add declarations or definitions to
namespace std or namespaces within namespace std unless otherwise
specified. A program may add template specializations for any standard
library template to namespace std. Such a specialization (complete or
partial) of a standard library template results in undefined behavior
unless the declaration depends on a user-defined name of external linkage
and unless the specialization meets the standard library requirements for
the original template.


Ah, and it's exactly template specializations that I had in mind. But
consider another case -- what if I wanted to define an operator<< for
vector? I ran into this a while back, and due to the way ADL/Koenig
lookup works, and the interface principle, it seems you'd want to put
that in ::std.


I have no idea what you mean by interface principle, but I ran into the ADL
issue myself when I defined operator<< for all the standard containers. As
far as I know, putting them within namespace std is not permitted because
they lack dependence "on a user-defined name of external linkage". And yes,
ADL caused problems for me in this case.
Best

Kai-Uwe Bux
Jan 31 '06 #17
Kai-Uwe Bux wrote:

I ran into the ADL
issue myself when I defined operator<< for all the standard containers. As
far as I know, putting them within namespace std is not permitted because
they lack dependence "on a user-defined name of external linkage". And yes,
ADL caused problems for me in this case.


I think this is fair though. In your case (within one particular
program), a stream operator for the standard containers might make
sense, but surely in general, it doesn't make sense.

The solution would be to define your own type, and the stream operator
for that. Obviously a typedef doesn't suffice - is the only solution to
wrap the container to produce your own type?
#include <vector>
#include <iostream>
#include <ios>

//#define OWNTYPE
#ifndef OWNTYPE
typedef std::vector<int> myType;
#else
class myType {
typedef std::vector<int> type;
type vec_;
public:
typedef std::vector<int>::const_iterator const_iterator;
const_iterator begin() const { return vec_.begin(); }
const_iterator end() const { return vec_.end(); }

myType(type::size_type count, const type::value_type& val) :
vec_(count, val) {};

type::value_type operator[](const type::size_type index) {
return vec_[index];
}
//etc.
};
#endif

std::ostream& operator<<(std::ostream& lhs, const myType& rhs) {
for (myType::const_iterator iter = rhs.begin(); iter != rhs.end();
++iter) {
lhs << *iter << "\n";
}
return lhs;
}

int main()
{
std::vector<int> v1(2,6);
myType v2(2,7);

#ifndef OWNTYPE
std::cout << v1 << std::endl;
#endif
std::cout << v2 << std::endl;
}

That's a lot of hassle.

Ben Pope
--
I'm not just a number. To many, I'm known as a string...
Jan 31 '06 #18
Ben Pope wrote:
Kai-Uwe Bux wrote:

I ran into the ADL
issue myself when I defined operator<< for all the standard containers.
As far as I know, putting them within namespace std is not permitted
because they lack dependence "on a user-defined name of external
linkage". And yes, ADL caused problems for me in this case.
I think this is fair though. In your case (within one particular
program), a stream operator for the standard containers might make
sense, but surely in general, it doesn't make sense.


I beg to differ: (a) "my case" is not one program, and (b) of course it
makes sense. Now, "my case" is mostly math programming. Here, I would use
operator<< and operator>> for stream IO in programming filters; that is the
output generated by operator<< would not be intended to be read by a human
but just by another program. On the other hand, most classes would also
have a

void dump_TeX ( void ) const;

method that will take care of proper formating for human readers.

The solution would be to define your own type, and the stream operator
for that. Obviously a typedef doesn't suffice - is the only solution to
wrap the container to produce your own type?

[code snipped]

Actually, a solution that I have been thinking about is to define my own
stream templates instead. Those will also fix another major hassle with
standard streams: operator<< and operator>> are not truly inverse
operations.
Best

Kai-Uwe Bux
Jan 31 '06 #19
Kai-Uwe Bux wrote:
Ben Pope wrote:
Kai-Uwe Bux wrote:

I ran into the ADL
issue myself when I defined operator<< for all the standard containers.
As far as I know, putting them within namespace std is not permitted
because they lack dependence "on a user-defined name of external
linkage". And yes, ADL caused problems for me in this case.
I think this is fair though. In your case (within one particular
program), a stream operator for the standard containers might make
sense, but surely in general, it doesn't make sense.


I beg to differ: (a) "my case" is not one program, and (b) of course it
makes sense. Now, "my case" is mostly math programming. Here, I would use
operator<< and operator>> for stream IO in programming filters; that is
the output generated by operator<< would not be intended to be read by a
human but just by another program. On the other hand, most classes would
also have a

void dump_TeX ( void ) const;


Oops, should be:

std::ostream& dump_TeX ( std::ostream& ) const;

method that will take care of proper formating for human readers.

The solution would be to define your own type, and the stream operator
for that. Obviously a typedef doesn't suffice - is the only solution to
wrap the container to produce your own type?

[code snipped]

Actually, a solution that I have been thinking about is to define my own
stream templates instead. Those will also fix another major hassle with
standard streams: operator<< and operator>> are not truly inverse
operations.
Best

Kai-Uwe Bux


Jan 31 '06 #20
Ben Pope wrote:
Kai-Uwe Bux wrote:

I ran into the ADL
issue myself when I defined operator<< for all the standard containers.
As far as I know, putting them within namespace std is not permitted
because they lack dependence "on a user-defined name of external
linkage". And yes, ADL caused problems for me in this case.


I think this is fair though. In your case (within one particular
program), a stream operator for the standard containers might make
sense, but surely in general, it doesn't make sense.

The solution would be to define your own type, and the stream operator
for that. Obviously a typedef doesn't suffice - is the only solution to
wrap the container to produce your own type?
#include <vector>
#include <iostream>
#include <ios>

//#define OWNTYPE
#ifndef OWNTYPE
typedef std::vector<int> myType;
#else
class myType {
typedef std::vector<int> type;
type vec_;
public:
typedef std::vector<int>::const_iterator const_iterator;
const_iterator begin() const { return vec_.begin(); }
const_iterator end() const { return vec_.end(); }

myType(type::size_type count, const type::value_type& val) :
vec_(count, val) {};

type::value_type operator[](const type::size_type index) {
return vec_[index];
}
//etc.
};
#endif

std::ostream& operator<<(std::ostream& lhs, const myType& rhs) {
for (myType::const_iterator iter = rhs.begin(); iter != rhs.end();
++iter) {
lhs << *iter << "\n";
}
return lhs;
}

int main()
{
std::vector<int> v1(2,6);
myType v2(2,7);

#ifndef OWNTYPE
std::cout << v1 << std::endl;
#endif
std::cout << v2 << std::endl;
}

That's a lot of hassle.


Ok, I had a closer look at your solution. It seems that you want to create a
different type for this std::vector<int> so that the C++ overloading
mechanism will handle things gracefully. I would suggest to just inherit
like so:

template < typename T >
struct math_vector : public std::vector< T > {

math_vector ( void )
: std::vector< T > ()
{}

template < typename A >
math_vector ( A a )
: std::vector< T > ( a )
{}

template < typename A, typename B >
math_vector ( A a, B b )
: std::vector< T > ( a, b )
{}

template < typename A, typename B, typename C >
math_vector ( A a, B b, C c )
: std::vector< T > ( a, b, c )
{}

template < typename A, typename B, typename C, typename D >
math_vector ( A a, B b, C c, D d )
: std::vector< T > ( a, b, c, d )
{}

}; // math_vector<>

Now, math_vector<int> and std::vector<int> are distinct types whose
interfaces are completely identical.

I used that to define arithmetic operators for vectors in my math programs
so that they would not apply to vectors that are just data structures.
Best

Kai-Uwe Bux
Jan 31 '06 #21
Kai-Uwe Bux wrote:
mechanism will handle things gracefully. I would suggest to just inherit
like so:

template < typename T >
struct math_vector : public std::vector< T > {
It is generally a *bad* idea to inherit from standard containers.
Please don't advocate this. See FAQ.
math_vector ( void )
: std::vector< T > ()
{}

template < typename A >
math_vector ( A a )
: std::vector< T > ( a )
{}

template < typename A, typename B >
math_vector ( A a, B b )
: std::vector< T > ( a, b )
{}

template < typename A, typename B, typename C >
math_vector ( A a, B b, C c )
: std::vector< T > ( a, b, c )
{}

template < typename A, typename B, typename C, typename D >
math_vector ( A a, B b, C c, D d )
: std::vector< T > ( a, b, c, d )
{}

}; // math_vector<>

Now, math_vector<int> and std::vector<int> are distinct types whose
interfaces are completely identical.


Wrong. There is no math_vector<int>::value_type, for example.

-shez-

Jan 31 '06 #22
Shezan Baig wrote:
Kai-Uwe Bux wrote:
mechanism will handle things gracefully. I would suggest to just inherit
like so:

template < typename T >
struct math_vector : public std::vector< T > {


It is generally a *bad* idea to inherit from standard containers.
Please don't advocate this. See FAQ.


I know about that FAQ. This, however, is one of those recommendations that
have a firm foundation in just one of the various programming paradigms
supported by C++. Namely, if you think OO, then it is a bad idea to inherit
from classes without virtual destructors. The reason is that, sooner or
later, you will find yourself deleting polymorphic pointers to objects in
your class hierarchy.

However, if you think, for instance, policy based design, you will find that
inheriting from classes without virtual destructors is quite common.
Extending advice beyond the scope of applicability is not good. In the case
under discussion (i.e., in the context of math programming), inheriting
from std::vector<> is absolutely fine: there is no use for polymorphic use
of pointers to those objects anyway, and if there was, the same warnings
that apply to std::vector will apply to math_vector. Inheritance, in this
case, is just an implementation detail.

math_vector ( void )
: std::vector< T > ()
{}

template < typename A >
math_vector ( A a )
: std::vector< T > ( a )
{}

template < typename A, typename B >
math_vector ( A a, B b )
: std::vector< T > ( a, b )
{}

template < typename A, typename B, typename C >
math_vector ( A a, B b, C c )
: std::vector< T > ( a, b, c )
{}

template < typename A, typename B, typename C, typename D >
math_vector ( A a, B b, C c, D d )
: std::vector< T > ( a, b, c, d )
{}

}; // math_vector<>

Now, math_vector<int> and std::vector<int> are distinct types whose
interfaces are completely identical.


Wrong. There is no math_vector<int>::value_type, for example.


Whereas your statement above is a matter of opinion, this is a matter of
fact. And you got it wrong: math_vector<int>::value_type is there. Just try
it.

#include <vector>

template < typename T >
struct math_vector : public std::vector< T > {

math_vector ( void )
: std::vector< T > ()
{}

template < typename A >
math_vector ( A a )
: std::vector< T > ( a )
{}

template < typename A, typename B >
math_vector ( A a, B b )
: std::vector< T > ( a, b )
{}

template < typename A, typename B, typename C >
math_vector ( A a, B b, C c )
: std::vector< T > ( a, b, c )
{}

template < typename A, typename B, typename C, typename D >
math_vector ( A a, B b, C c, D d )
: std::vector< T > ( a, b, c, d )
{}

}; // math_vector<>

int main ( void ) {
math_vector<int>::value_type a;
}

compiles without problems. If you think that this is not standard
conformant, please cite chapter and verse.

Best

Kai-Uwe Bux
Jan 31 '06 #23
Kai-Uwe Bux wrote:
Wrong. There is no math_vector<int>::value_type, for example.


Whereas your statement above is a matter of opinion, this is a matter of
fact. And you got it wrong: math_vector<int>::value_type is there. Just try
it.

You are right, sorry for judging so quickly.

-shez-

Jan 31 '06 #24

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: mjm | last post by:
Folks, Please help me with the following problems: ******************************************** 1. I have a class template template<class Base> class Matrix : public Base { /* .... */ }
1
by: qazmlp | last post by:
Is it a good style to re-forward declare the types, which were already forward declared in base header file, in derived class header file? // base.h class addrClass; class base { public:...
7
by: Lynn | last post by:
I am rewriting some memory management code and I need to have a forward declaration of a data structure. I am not converting this data structure into a class (yet). How do I generate a forward...
5
by: Simon Elliott | last post by:
For some time I've been using typedefs for STL containers for reasons outlined in: http://www.gotw.ca/gotw/046.htm However a major downside to this is that you can't forward declare a typedef...
1
by: aurgathor | last post by:
Howdy, I got the templatized class Matrix working in the way it's written in the faq , and it works fine as: Matrix<sqr_T> display(80,25); However, I'd like to have this variable in the...
7
by: Noah Roberts | last post by:
I have something like the following: template<ENUM X, int I = 0> class Generic { .... }; typedef Generic<Val1> v1_type; typedef Generic<Val2> v2_type;
2
by: flopbucket | last post by:
Hi, How can I forward declare a class that is in a different namespace? As a simple example: classs Foo { std::string *string; };
6
by: Hunk | last post by:
Hi I have a question on usage of forward declarations of templates. While searching through this group , people suggested using forward declarations and typedefs for templates as // in...
5
by: aaragon | last post by:
Hello, I was wondering if it is possible to forward declare a type definition. If so, what is the way to do it? I found a couple of messages where they say it's not possible but there must be a way...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.