Forward declare a templatized class

Discussion in 'C++' started by mark.moore@notlimited.com, Jan 28, 2006.

  1. Guest

    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 28, 2006
    #1
    1. Advertising

  2. Ben Radford Guest

    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 Radford, Jan 29, 2006
    #2
    1. Advertising

  3. Guest

    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, 2006
    #3
  4. Luke Meyers Guest

    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 Meyers, Jan 29, 2006
    #4
  5. Guest

    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, 2006
    #5
  6. Guest

    Ben, after re-reading your post, I realized you hadt exactly right.
    Sorry for misunderstanding it at first. -MM
    , Jan 29, 2006
    #6
  7. Luke Meyers Guest

    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, Jan 29, 2006
    #7
  8. Guest

    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, 2006
    #8
  9. Luke Meyers Guest

    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, Jan 30, 2006
    #9
  10. Guest

    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>::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.
    , Jan 30, 2006
    #10
  11. Luke Meyers Guest

    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
    Luke Meyers, Jan 30, 2006
    #11
  12. Marcus Kwok Guest

    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, Jan 30, 2006
    #12
  13. Luke Meyers Guest

    Marcus Kwok wrote:
    > 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, Jan 30, 2006
    #13
  14. Marcus Kwok Guest

    Luke Meyers <> wrote:
    > Marcus Kwok wrote:
    >> 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..._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.

    --
    Marcus Kwok
    Marcus Kwok, Jan 30, 2006
    #14
  15. Kai-Uwe Bux Guest

    Luke Meyers wrote:

    > Marcus Kwok wrote:
    >> 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, Jan 30, 2006
    #15
  16. Luke Meyers Guest

    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, Jan 31, 2006
    #16
  17. Kai-Uwe Bux Guest

    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, Jan 31, 2006
    #17
  18. Ben Pope Guest

    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::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
    --
    I'm not just a number. To many, I'm known as a string...
    Ben Pope, Jan 31, 2006
    #18
  19. Kai-Uwe Bux Guest

    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, Jan 31, 2006
    #19
  20. Kai-Uwe Bux Guest

    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::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
    Kai-Uwe Bux, Jan 31, 2006
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. hall
    Replies:
    2
    Views:
    285
  2. amit gulati

    forward declare a template class

    amit gulati, Nov 17, 2003, in forum: C++
    Replies:
    1
    Views:
    453
    Victor Bazarov
    Nov 17, 2003
  3. mrstephengross
    Replies:
    5
    Views:
    580
    Larry I Smith
    May 18, 2005
  4. Replies:
    4
    Views:
    3,758
    Neil Cerutti
    Nov 29, 2005
  5. Daniel T.
    Replies:
    1
    Views:
    314
    Kai-Uwe Bux
    Jan 20, 2009
Loading...

Share This Page