class String : public std::string {}; ????

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Angus Leeming

    class String : public std::string {}; ????

    Hello,

    Could someone explain to me why the Standard conveners chose to typedef
    std::string rather than derive it from std::basic_stri ng<char, ...>?

    The result of course is that it is effectively impossible to forward declare
    std::string. (Yes I am aware that some libraries have a string_fwd.h header,
    but this is not portable.)

    That said, is there any real reason why I can't derive an otherwise empty
    String class from std::string? Will there be any hidden surprises? As I
    understand it, the fact that String has no member variables of its own means
    that the non-virtual std::string destructor is not an issue.

    Nonetheless, I'm wary because I have heard so much about 'std::string is not
    designed to be derived from'. Nobody has explained the rationale behind such
    a statement to me however.

    Below is sample code that appears to work just fine.

    Best regards,
    Angus



    #ifndef FWD_DECLARABLE_ STRING_H
    #define FWD_DECLARABLE_ STRING_H

    #include <string>

    struct String : public std::string {
    typedef std::string base_type;

    String() {}
    String(base_typ e const & other)
    : base_type(other ) {}
    explicit String(allocato r_type const & al)
    : base_type(al) {}
    String(String const & other, size_type off, size_type count = npos)
    : base_type(other , off, count) {}
    String(String const & other, size_type off, size_type count,
    allocator_type const & al)
    : base_type(other , off, count, al) {}
    String(value_ty pe const * ptr, size_type count)
    : base_type(ptr, count) {}
    String(value_ty pe const * ptr, size_type count, allocator_type const & al)
    : base_type(ptr, count, al) {}
    String(value_ty pe const * ptr)
    : base_type(ptr) {}
    String(value_ty pe const * ptr, allocator_type const & al)
    : base_type(ptr, al) {}
    String(size_typ e count, value_type ch)
    : base_type(count , ch) {}
    String(size_typ e count, value_type ch, allocator_type const & al)
    : base_type(count , ch, al) {}
    template <typename InIt>
    String(InIt first, InIt last)
    : base_type(first , last) {}
    template <typename InIt>
    String(InIt first, InIt last, allocator_type const & al)
    : base_type(first , last, al) {}

    String & operator=(value _type const * ptr)
    { base_type::oper ator=(ptr); }
    String & operator=(value _type ch)
    { base_type::oper ator=(ch); }
    };

    #endif // FWD_DECLARABLE_ STRING_H



    #include <iostream>

    int main()
    {
    String const angus = "angus";
    std::string const jmarc = "jmarc";
    String test1;
    std::string test2;

    // Assign a std::string to a String
    test1 = jmarc;
    std::cout << "test1 " << test1 << std::endl;

    // Assign a String to a std::string
    test2 = angus;
    std::cout << "test2 " << test2 << std::endl;

    // Assign a String to a String (implicit assignment operator)
    test1 = angus;
    std::cout << "test1 " << test1 << std::endl;

    // Implicit copy c-tor;
    String const test3 = angus;
    std::cout << "test3 " << test3 << std::endl;

    // Copy construct a std::string from a String
    std::string test4 = angus;
    std::cout << "test4 " << test4 << std::endl;

    return 0;
    }

  • llewelly

    #2
    Re: class String : public std::string {}; ????

    Angus Leeming <angus.leeming@ btopenworld.com > writes:
    [color=blue]
    > Hello,
    >
    > Could someone explain to me why the Standard conveners chose to typedef
    > std::string rather than derive it from std::basic_stri ng<char, ...>?
    >
    > The result of course is that it is effectively impossible to forward declare
    > std::string. (Yes I am aware that some libraries have a string_fwd.h header,
    > but this is not portable.)
    >
    > That said, is there any real reason why I can't derive an otherwise empty
    > String class from std::string?[/color]

    There are two issues.

    (a) No virtual destructor. This means you can't safely call delete on
    a pointer to string when the object pointed to is in fact a
    derived_from_st ring.

    (b) It isn't designed for polymorphism. (Like most of the standard
    library.) Public inheritance is usually used for
    polymorphism. Note (a) is a symptom of (b) .

    If you somehow know you'll never use the derived_from_st ring
    polymorphically , you won't encounter problems.

    However, before proceeding, try private inheritance combined with a
    few well-placed using statements. That is safer.

    [color=blue]
    > Will there be any hidden surprises?[/color]

    See above. Further, if people *can* make polymorphic use of your
    derived_from_st ring type, they all too often will.
    [color=blue]
    > As I understand it, the fact that String has no member variables of
    > its own means that the non-virtual std::string destructor is not an
    > issue.[/color]
    [snip]

    This has nothing to do with it. Calling delete on a pointer to string
    is undefined behavior if it in fact points to a
    derived_from_st ring, regardless of whether derived_from_st ring has
    member variables of its own.

    Comment

    • Ron Natalie

      #3
      Re: class String : public std::string {}; ????


      "Angus Leeming" <angus.leeming@ btopenworld.com > wrote in message news:bjnh1d$r5e $1@hercules.bti nternet.com...
      [color=blue]
      >
      > That said, is there any real reason why I can't derive an otherwise empty
      > String class from std::string? Will there be any hidden surprises? As I
      > understand it, the fact that String has no member variables of its own means
      > that the non-virtual std::string destructor is not an issue.[/color]

      What makes you think it's not an issue? It is undefined behavior to delete
      a derived object from a pointer to its base class without a virtual distructor.
      It doesn't matter that the derived destructor doesn't do anything or that there
      are no data members in the derived class.



      Comment

      • Angus Leeming

        #4
        Re: class String : public std::string {}; ????

        Ron Natalie wrote:
        [color=blue]
        >
        > "Angus Leeming" <angus.leeming@ btopenworld.com > wrote in message
        > news:bjnh1d$r5e $1@hercules.bti nternet.com...
        >[color=green]
        >>
        >> That said, is there any real reason why I can't derive an otherwise empty
        >> String class from std::string? Will there be any hidden surprises? As I
        >> understand it, the fact that String has no member variables of its own
        >> means that the non-virtual std::string destructor is not an issue.[/color]
        >
        > What makes you think it's not an issue? It is undefined behavior to delete
        > a derived object from a pointer to its base class without a virtual
        > distructor. It doesn't matter that the derived destructor doesn't do
        > anything or that there are no data members in the derived class.[/color]

        Many thanks to both of you for the clear explanation. I shall put your
        arguments to the project moderator who will promptly boot my proposed use of
        String into touch ;-)

        Thanks again,
        Angus

        Comment

        • Ivan Vecerina

          #5
          Re: class String : public std::string {}; ????

          Hi Angus,

          | Could someone explain to me why the Standard conveners chose to typedef
          | std::string rather than derive it from std::basic_stri ng<char, ...>?

          Two reasons I can see:
          - If this were done, template code that uses std::basic_stri ng<ACharType>
          would be using a different string class than your non-template code ?
          Or some special policy classes would be needed...
          - If such derivation was used, std::basic_stri ng would need a virtual
          destructor.

          | The result of course is that it is effectively impossible to forward
          declare
          | std::string. (Yes I am aware that some libraries have a string_fwd.h
          header,
          | but this is not portable.)
          True. Note that adding such a header yourself is an option even for
          implementations that do not support it...

          | That said, is there any real reason why I can't derive an otherwise empty
          | String class from std::string? Will there be any hidden surprises? As I
          | understand it, the fact that String has no member variables of its own
          | means that the non-virtual std::string destructor is not an issue.
          It *might* not be an issue in most C++ implementations . But formally,
          it still does lead to undefined behavior when used improperly.
          The following related discussion might be of interest:



          | Nonetheless, I'm wary because I have heard so much about 'std::string is
          not
          | designed to be derived from'. Nobody has explained the rationale behind
          such
          | a statement to me however.

          The public non-virtual destructor remains the key issue.
          Another caveat is the too many member functions of std::string,
          which may at some point create conflicts...

          Most of the time, people who want to derive from std::string also want to
          add some state/data members to it (in which case containment is a better
          idea, as it allows to enforce class invariants), or add some member
          functions (in that case, non-member functions should be preferred).
          Public derivation from std::string has serious caveats,
          and is often done by novices for these wrong reasons.
          And indeed, I have yet to see a good motivation for deriving from
          std::string...

          | Below is sample code that appears to work just fine.
          From a quick look through the code it seems ok and will not invoke UB.
          However, is it worth defining your own class just to be
          able to forward-declare it ?
          Force any maintainers to use an additional non-standard class,
          and deal with (even implicit) conversions ?

          Personally, if compile-time overhead was a read problem,
          would rather implement a custom <string_fwd.h > header for
          each platform I am using...


          Regards,
          Ivan
          --
          Ivan Vecerina - expert in medical devices, software - info, links, contact information, code snippets

          Brainbench MVP for C++ <> http://www.brainbench.com


          Comment

          • tom_usenet

            #6
            Re: class String : public std::string {}; ????

            On Wed, 10 Sep 2003 15:46:53 +0000 (UTC), Angus Leeming
            <angus.leeming@ btopenworld.com > wrote:
            [color=blue]
            >Hello,
            >
            >Could someone explain to me why the Standard conveners chose to typedef
            >std::string rather than derive it from std::basic_stri ng<char, ...>?
            >
            >The result of course is that it is effectively impossible to forward declare
            >std::string. (Yes I am aware that some libraries have a string_fwd.h header,
            >but this is not portable.)[/color]

            Why do you need to forward declare string? Have you measured an actual
            increase in compile time when you #include <string>?

            Forward declarations are generally used to decouple code and reduce
            dependencies. However, a dependency on std::string isn't a dependency
            at all, it is just a use of the language. IOW, since std::string only
            changes if you upgrade your compiler (in which case a full recompile
            is required anyway), there isn't much to be gained from a forward
            declaration.
            [color=blue]
            >That said, is there any real reason why I can't derive an otherwise empty
            >String class from std::string? Will there be any hidden surprises? As I
            >understand it, the fact that String has no member variables of its own means
            >that the non-virtual std::string destructor is not an issue.[/color]

            In practice this is true on most compilers. However, it is dangerous
            to rely on it since the standard forbids it.
            [color=blue]
            >Nonetheless, I'm wary because I have heard so much about 'std::string is not
            >designed to be derived from'. Nobody has explained the rationale behind such
            >a statement to me however.[/color]

            The idea is that there is generally nothing to be gained from deriving
            from std::string, and there is danger (the non-virtual destructor). If
            you need to:

            1. Add features.
            Add them as non-member functions.
            2. Add members.
            Create a class that contains a string and your extra members or use
            private inheritence - typically you won't want the full string
            interface anyway (often combined with 3).
            3. Add invariants.
            You can't use public derivation for this, you must use private
            inheritence or containment, since otherwise the invariants can be
            broken by accessing the object as a string&.
            4. Override behaviour.
            Inheritence doesn't work since none of the methods are virtual.
            5. Forward declare String (!)
            Just #include <string>.

            Tom

            Comment

            • Angus Leeming

              #7
              Re: class String : public std::string {}; ????

              Ivan Vecerina wrote:[color=blue]
              > | That said, is there any real reason why I can't derive an otherwise empty
              > | String class from std::string? Will there be any hidden surprises? As I
              > | understand it, the fact that String has no member variables of its own
              > | means that the non-virtual std::string destructor is not an issue.
              > It *might* not be an issue in most C++ implementations . But formally,
              > it still does lead to undefined behavior when used improperly.
              > The following related discussion might be of interest:
              > http://www.gotw.ca/publications/mill18.htm[/color]

              Thanks for the pointer.
              [color=blue]
              > Most of the time, people who want to derive from std::string also want to
              > add some state/data members to it (in which case containment is a better
              > idea, as it allows to enforce class invariants), or add some member
              > functions (in that case, non-member functions should be preferred).[/color]

              Understood.
              [color=blue]
              > Personally, if compile-time overhead was a read problem,
              > would rather implement a custom <string_fwd.h > header for
              > each platform I am using...[/color]

              Guess what my next proposal to the project is going to be ;-)

              Many thanks to you for your time; it is much appreciated.
              Regards,
              Angus

              Comment

              • Angus Leeming

                #8
                Re: class String : public std::string {}; ????

                tom_usenet wrote:
                [color=blue][color=green]
                >>The result of course is that it is effectively impossible to forward declare
                >>std::string . (Yes I am aware that some libraries have a string_fwd.h header,
                >>but this is not portable.)[/color]
                >
                > Why do you need to forward declare string? Have you measured an actual
                > increase in compile time when you #include <string>?[/color]

                That's exactly what I am going to do. I am talking about a project (LyX,
                www.lyx.org) that takes 26mins on a 2.7GHz machine to do a full compile. It
                gets a little wearing at times, so we all start hunting around for
                scapegoats. Yeh, I know. We should write decent code in the first place ;-)

                Regards,
                Angus

                Comment

                • André Pönitz

                  #9
                  Re: class String : public std::string {}; ????

                  llewelly <llewelly.at@xm ission.dot.com> wrote:[color=blue][color=green]
                  >> The result of course is that it is effectively impossible to forward declare
                  >> std::string. (Yes I am aware that some libraries have a string_fwd.h header,
                  >> but this is not portable.)
                  >>
                  >> That said, is there any real reason why I can't derive an otherwise empty
                  >> String class from std::string?[/color]
                  >
                  > There are two issues.
                  >
                  > (a) No virtual destructor. This means you can't safely call delete on
                  > a pointer to string when the object pointed to is in fact a
                  > derived_from_st ring.
                  >
                  > (b) It isn't designed for polymorphism. (Like most of the standard
                  > library.) Public inheritance is usually used for
                  > polymorphism. Note (a) is a symptom of (b) .
                  >
                  > If you somehow know you'll never use the derived_from_st ring
                  > polymorphically , you won't encounter problems.[/color]

                  Is there a way to make sure that a class will never be used for
                  inheritance? [I don't think so but I thought I better ask]
                  [color=blue]
                  > However, before proceeding, try private inheritance combined with a
                  > few well-placed using statements. That is safer.[/color]

                  This would make both 'issues' go away, wouldn't it?

                  So to re-phrase Angus' original question: Why is std::string a typedef
                  and not _privately_ derived from std::basic_stri ng<char> with a lots of
                  'using' in it? [The benefit would still be there: to be able to
                  forward-declare it]

                  Or, alternatively:

                  Why isn't there a 'slim' Standard <stringfwd> header?

                  Andre'

                  Comment

                  • tom_usenet

                    #10
                    Re: class String : public std::string {}; ????

                    On Wed, 10 Sep 2003 16:57:27 +0000 (UTC), Angus Leeming
                    <angus.leeming@ btopenworld.com > wrote:
                    [color=blue]
                    >tom_usenet wrote:
                    >[color=green][color=darkred]
                    >>>The result of course is that it is effectively impossible to forward declare
                    >>>std::strin g. (Yes I am aware that some libraries have a string_fwd.h header,
                    >>>but this is not portable.)[/color]
                    >>
                    >> Why do you need to forward declare string? Have you measured an actual
                    >> increase in compile time when you #include <string>?[/color]
                    >
                    >That's exactly what I am going to do. I am talking about a project (LyX,
                    >www.lyx.org) that takes 26mins on a 2.7GHz machine to do a full compile. It
                    >gets a little wearing at times, so we all start hunting around for
                    >scapegoats. Yeh, I know. We should write decent code in the first place ;-)[/color]

                    I don't think GCC supports pre-compiled headers, which might help for
                    system and standard headers. GCC is a very slow compiler though at the
                    best of times.

                    Otherwise, it's just a matter of reducing the real dependencies in
                    your code, so less of your own headers are included in each file.

                    Tom

                    Comment

                    • Shane Beasley

                      #11
                      Re: class String : public std::string {}; ????

                      André Pönitz <poenitz@gmx.ne t> wrote in message news:<bjnla2$8b a$1@anderson.hr z.tu-chemnitz.de>...
                      [color=blue]
                      > Is there a way to make sure that a class will never be used for
                      > inheritance? [I don't think so but I thought I better ask][/color]



                      [color=blue][color=green]
                      > > However, before proceeding, try private inheritance combined with a
                      > > few well-placed using statements. That is safer.[/color]
                      >
                      > This would make both 'issues' go away, wouldn't it?[/color]

                      Mostly, yes. A private inheritance relationship is known only to the
                      derived class and its friends, so only they can use it
                      polymorphically . If they don't use it polymorphically , there is no
                      problem.
                      [color=blue]
                      > So to re-phrase Angus' original question: Why is std::string a typedef
                      > and not _privately_ derived from std::basic_stri ng<char> with a lots of
                      > 'using' in it? [The benefit would still be there: to be able to
                      > forward-declare it][/color]

                      A few reasons. First and foremost, it wouldn't be a std::basic_stri ng
                      instantation anymore, so it wouldn't work with templates that expect
                      std::basic_stri ng. (I'm sure I could come up with other forward
                      declarations that might do worse.)

                      Second, it's a hack. I've used private inheritance and using
                      declarations to implement a new class in terms of an old one. But I'd
                      never recommend using them to fake forward declarations, certainly not
                      at the level of the Standard.
                      [color=blue]
                      > Or, alternatively:
                      >
                      > Why isn't there a 'slim' Standard <stringfwd> header?[/color]

                      Since we've got <iosfwd>, my guess is that they just forgot to put one
                      in. Sounds like something to be asked and/or debated on comp.std.c++.
                      :)

                      ---

                      Incidentally, you can (depending on your intestinal fortitude)
                      forward-declare these things yourself. Herb Sutter tells us not to do
                      it in GOTW 34:

                      <quote url="http://www.gotw.ca/gotw/034.htm">
                      The standard says:

                      It is undefined for a C++ program to add declarations or definitions
                      to namespace std or namespaces within namespace std unless otherwise
                      specified.
                      </quote>

                      Of course, in the Real World, without which ISO C++ is meaningless,
                      I'd love to see an implementation which does anything worse than fail
                      to compile on a forward-declaration of std::string.

                      Herb continues:

                      <quote>
                      Among other things, this allows vendors to provide implementations of
                      the standard library that have more template parameters for library
                      templates than the standard requires (suitably defaulted, of course,
                      to remain compatible).
                      </quote>

                      But according to the official ISO C++ closed issues list, they can't:

                      <quote url="http://std.dkuug.dk/jtc1/sc22/wg21/docs/lwg-closed.html#94" >
                      Library implementors are not permitted to add template parameters to
                      standard library classes.
                      </quote>

                      So, any compiler that doesn't supply the *exact* template interface is
                      non-conforming. If we know the interface, we ought to be able to
                      forward-declare it, like so:

                      namespace std {
                      template <typename> class char_traits;
                      template <typename> class allocator;

                      // cannot use default parameters in the forward declaration
                      template <typename, typename, typename> class basic_string;

                      // instead, use them here
                      typedef std::basic_stri ng<char,
                      std::char_trait s<char>,
                      std::allocator< char> > string;
                      }

                      But since this is heresy in comp.lang.c++, I suppose you're left to
                      including <string> whenever you need std::string, at least for now.

                      Better go get some coffee... This could take awhile. :)

                      - Shane

                      Comment

                      Working...