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? 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?"
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? 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
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?
Ben, after re-reading your post, I realized you hadt exactly right.
Sorry for misunderstanding it at first. -MM 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
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 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
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. 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 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
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
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
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
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
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
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...
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
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
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
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-
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
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- This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
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 { /* .... */ }
|
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:...
|
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...
|
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...
|
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...
|
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;
|
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;
};
|
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...
|
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...
|
by: Charles Arthur |
last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
|
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
|
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...
|
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...
|
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,...
|
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...
|
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,...
|
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...
|
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,...
| |