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

Discussion in 'C++' started by Angus Leeming, Sep 10, 2003.

  1. Hello,

    Could someone explain to me why the Standard conveners chose to typedef
    std::string rather than derive it from std::basic_string<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_type const & other)
    : base_type(other) {}
    explicit String(allocator_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_type const * ptr, size_type count)
    : base_type(ptr, count) {}
    String(value_type const * ptr, size_type count, allocator_type const & al)
    : base_type(ptr, count, al) {}
    String(value_type const * ptr)
    : base_type(ptr) {}
    String(value_type const * ptr, allocator_type const & al)
    : base_type(ptr, al) {}
    String(size_type count, value_type ch)
    : base_type(count, ch) {}
    String(size_type 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::eek:perator=(ptr); }
    String & operator=(value_type ch)
    { base_type::eek:perator=(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;
    }
     
    Angus Leeming, Sep 10, 2003
    #1
    1. Advertising

  2. Angus Leeming

    Ron Natalie Guest

    "Angus Leeming" <> wrote in message news:bjnh1d$r5e$...

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


    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.
     
    Ron Natalie, Sep 10, 2003
    #2
    1. Advertising

  3. Angus Leeming

    llewelly Guest

    Angus Leeming <> writes:

    > Hello,
    >
    > Could someone explain to me why the Standard conveners chose to typedef
    > std::string rather than derive it from std::basic_string<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?


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

    (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_string
    polymorphically, you won't encounter problems.

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


    > Will there be any hidden surprises?


    See above. Further, if people *can* make polymorphic use of your
    derived_from_string type, they all too often will.

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

    [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_string, regardless of whether derived_from_string has
    member variables of its own.
     
    llewelly, Sep 10, 2003
    #3
  4. Ron Natalie wrote:

    >
    > "Angus Leeming" <> wrote in message
    > news:bjnh1d$r5e$...
    >
    >>
    >> 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.

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


    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
     
    Angus Leeming, Sep 10, 2003
    #4
  5. Hi Angus,

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

    Two reasons I can see:
    - If this were done, template code that uses std::basic_string<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_string 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:
    http://www.gotw.ca/publications/mill18.htm


    | 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
    --
    http://ivan.vecerina.com
    Brainbench MVP for C++ <> http://www.brainbench.com
     
    Ivan Vecerina, Sep 10, 2003
    #5
  6. Ivan Vecerina wrote:
    > | 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


    Thanks for the pointer.

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


    Understood.

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


    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
     
    Angus Leeming, Sep 10, 2003
    #6
  7. Angus Leeming

    tom_usenet Guest

    On Wed, 10 Sep 2003 15:46:53 +0000 (UTC), Angus Leeming
    <> wrote:

    >Hello,
    >
    >Could someone explain to me why the Standard conveners chose to typedef
    >std::string rather than derive it from std::basic_string<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.)


    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.

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


    In practice this is true on most compilers. However, it is dangerous
    to rely on it since the standard forbids it.

    >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 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
     
    tom_usenet, Sep 10, 2003
    #7
  8. tom_usenet wrote:

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

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


    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
     
    Angus Leeming, Sep 10, 2003
    #8
  9. llewelly <> wrote:
    >> 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?

    >
    > 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_string.
    >
    > (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_string
    > polymorphically, you won't encounter problems.


    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]

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


    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_string<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'
     
    =?iso-8859-1?Q?Andr=E9_P=F6nitz?=, Sep 10, 2003
    #9
  10. Angus Leeming

    tom_usenet Guest

    On Wed, 10 Sep 2003 16:57:27 +0000 (UTC), Angus Leeming
    <> wrote:

    >tom_usenet wrote:
    >
    >>>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.)

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

    >
    >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 ;-)


    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
     
    tom_usenet, Sep 11, 2003
    #10
  11. André Pönitz <> wrote in message news:<bjnla2$8ba$-chemnitz.de>...

    > 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]


    http://www.parashift.com/c -faq-lite/strange-inheritance.html#faq-23.8
    http://www.research.att.com/~bs/bs_faq2.html#no-derivation

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

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


    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.

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


    A few reasons. First and foremost, it wouldn't be a std::basic_string
    instantation anymore, so it wouldn't work with templates that expect
    std::basic_string. (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.

    > Or, alternatively:
    >
    > Why isn't there a 'slim' Standard <stringfwd> header?


    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_string<char,
    std::char_traits<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
     
    Shane Beasley, Sep 11, 2003
    #11
    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. Charles A. Lackman
    Replies:
    1
    Views:
    1,437
    smith
    Dec 8, 2004
  2. SpamProof
    Replies:
    0
    Views:
    634
    SpamProof
    Oct 21, 2003
  3. Peter Jansson
    Replies:
    5
    Views:
    6,423
    Ivan Vecerina
    Mar 17, 2005
  4. DaveLessnau
    Replies:
    3
    Views:
    447
    Howard
    May 16, 2005
  5. Jeffrey Walton
    Replies:
    10
    Views:
    973
    Mathias Gaunard
    Nov 26, 2006
Loading...

Share This Page