1

I know that std::string is not designed for inheritance, however, I wonder why this class definition doesn't compile:

using std::string;
class ExtendedString: public string
{
public:
    using string::string;
    ExtendedString left(size_type n) const {
        return substr(0, n>size()?size():n);
    }
};

I get this error:

../../src/capel-tool.cpp:21:17: error: could not convert ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::substr(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type) const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int](0, ((n > ((const ExtendedString*)this)->ExtendedString::<anonymous>.std::__cxx11::basic_string<char>::size()) ? ((const ExtendedString*)this)->ExtendedString::<anonymous>.std::__cxx11::basic_string<char>::size() : n))’ from ‘std::__cxx11::basic_string<char>’ to ‘ExtendedString’

I expected that the left function would use any default constructor from std::string.

Even if I add an explicit constructor like this:

using std::string;
class ExtendedString: public string
{
public:
    using string::string;
    ExtendedString left(size_type n) const {
        return substr(0, n>size()?size():n);
    }

    explicit ExtendedString(const std::string &s);
};

I still get the same error.

Only when I add a normal constructor:

using std::string;
class ExtendedString: public string
{
public:
    using string::string;
    ExtendedString(const std::string &s);
    ExtendedString left(size_type n) const {
        return substr(0, n>size()?size():n);
    }
};

The code compiles ok.

Now imagine I want to make the same with a class that is designed for inheritance. Can not I just create this kind of wrapper in a way that the classes can be used interchangeablily without the need to create constructors thad would be created and called instead of being only "syntax sugar"

EDIT 1:

This suggested question does explain why my code does not work, but it does not propose any alternatives, as the accepted question does.

Santilín
  • 88
  • 8
  • 2
    The `explicit` version should work if you use it: `return ExtendedString{substr(0, n>size()?size():n)};` – Ted Lyngmo Jun 25 '23 at 13:29
  • 3
    An explicit constructor needs to be invoked explicitly. If you are attempting something like extension methods, I suggest a wrapper struct or a freestanding function. (C++23 has another nice alternative with deduced this, if you can use that.) – Eljay Jun 25 '23 at 13:30
  • 1
    Does this answer your question? [Why "an inherited constructor is not a candidate for initialization from an expression of the same or derived type"?](https://stackoverflow.com/questions/57926023/why-an-inherited-constructor-is-not-a-candidate-for-initialization-from-an-expr) – Nelfeal Jun 25 '23 at 13:35
  • 1
    If a class is designed for inheritance, then it is probably not designed for copying. – n. m. could be an AI Jun 25 '23 at 13:41

2 Answers2

5

The problem with your code is that substr returns a std::string, not an ExtendedString. No implicit conversion exists between the types, so that return statement cannot compile. There is an implicit conversion up the inheritance hierarchy, i.e. you can convert ExtendedString to std::string, but not the other way around.

To solve this for just your left function:

  • you could make the constructor implicit: ExtendedString(const std::string&)
  • construct explicitly: return ExtendedString(substr(...))

However, in general, what you're trying to do is just not nicely possible in C++. Every member function that you inherit from std::string is not going to return ExtendedString, so you have the wrong interface. C++ gives you no way of defining extension methods, which is the feature you're imitating.

"Clean" Alternatives

  • create an implicit conversion from std::string to ExtendedString, but this results in unnecessary copying
    • note: this is what you've done when declaring ExtendedString(const std::string &s);
  • inherit privately from std::string or better yet, wrap it as a member
    • create wrapper functions for every member function that std::string has, and make them take/return ExtendedString where necessary
  • just create free functions (see below)
std::string left(const std::string& s, std::size_t n) {
    return s.substr(0, std::min(s.size(), n));
}

As annoying as it is to sometimes call str.function() and sometimes function(str), that inconsistency is a small price compared to any alternative.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
-1
class ExtendedString
{
private:
    std::string data_;

public:
    ExtendedString() = default;
    ExtendedString(const std::string& s) : data_(s) {}

    ExtendedString left(std::string::size_type n) const {
        return ExtendedString(data_.substr(0, n > data_.size() ? data_.size() : n));
    }

    // Forward other methods as needed
};
  • Thaks for your reply. The problem with it is that there is a copy of s in the constructor so this is not syntax sugar. Another problem is that I have to implement lots of methods form std::string and I'd like the compiler to do that for me. – Santilín Jun 25 '23 at 15:43
  • @Santilín I understand your concerns. If you want to avoid unnecessary copying and minimize the manual implementation of methods from `std::string`, you can consider using a different approach. Instead of inheriting from `std::string`, you can create a wrapper class that contains a reference or pointer to `std::string` and provides a subset of its interface. ExtendedString(const std::string& s) : str_(s) {} ExtendedString left(std::string::size_type n) const { return ExtendedString(str_.substr(0, n > str_.size() ? str_.size() : n)); – Chloe Harper Jun 25 '23 at 15:50
  • 1
    Welcome to Stack Overflow! Most or all of your (currently) 19 answers (and most all of your comment replies as well) appear likely to have been entirely or partially written by AI (e.g., ChatGPT), and **far** too many of the ones I've checked so far appear to have errors that have been pointed out in the comments. Please be aware that [posting of AI-generated content is banned here](//meta.stackoverflow.com/q/421831). If you used an AI tool to assist with any answer, I would encourage you to delete it. Thanks! – NotTheDr01ds Jun 26 '23 at 21:37
  • 1
    **Readers should review this answer carefully and critically, as AI-generated information often contains fundamental errors and misinformation.** If you observe quality issues and/or have reason to believe that this answer was generated by AI, please leave feedback accordingly. The moderation team can use your help to identify quality issues. – NotTheDr01ds Jun 26 '23 at 21:37