Forward declare a templatized class

M

mark.moore

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?
 
B

Ben Radford

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.
 
M

mark.moore

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?
 
L

Luke Meyers

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
 
M

mark.moore

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?
 
M

mark.moore

Ben, after re-reading your post, I realized you hadt exactly right.
Sorry for misunderstanding it at first. -MM
 
L

Luke Meyers

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
but said:
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).

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
 
M

mark.moore

Luke said:
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.)

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.

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
 
L

Luke Meyers

Luke said:
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
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 said:
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
 
M

mark.moore

Wondering why you didn't have anything to say here. Assuming it's
because you agreed... :)

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.

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>::eek:perator=(str); return *this;
}
better_string& operator=(const value_type* str)
{ basic_string<value_type>::eek:perator=(str); return *this;
}
better_string& operator=(value_type c)
{ basic_string<value_type>::eek:perator=(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.
 
L

Luke Meyers

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?
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.
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
 
L

Luke Meyers

Marcus said:
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
 
M

Marcus Kwok

Luke Meyers said:
Marcus said:
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..._frm/thread/6aaec7bab56ddcf0/7300ffda21bb3bb4

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.
 
K

Kai-Uwe Bux

Luke said:
Marcus said:
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
 
L

Luke Meyers

Kai-Uwe Bux said:
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
 
K

Kai-Uwe Bux

Luke said:
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
 
B

Ben Pope

Kai-Uwe Bux said:
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::eek:stream& operator<<(std::eek:stream& 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
 
K

Kai-Uwe Bux

Ben said:
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
 
K

Kai-Uwe Bux

Kai-Uwe Bux said:
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::eek:stream& dump_TeX ( std::eek:stream& ) 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
 

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

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top