-1

I have a derived class which I want to be able to construct using the copy constructor where the argument is an instance of the base class.

I am sure this should be possible in C++. Here is an example:

#include <string>


class Base
{

public:

    friend
    void swap(Base& l, Base& r)
    {
        using std::swap;

        swap(l.a, r.a);
    }

    Base()
        : a{1}
    {
    }

    Base(const int a)
        : a{a}
    {
    }

    virtual
    ~Base()
    {
    }

    Base(const Base& base)
        : a{base.a}
    {
    }

    Base(Base&& base)
        : Base()
    {
        swap(*this, base);
    }

    Base& operator=(Base base)
    {
        swap(*this, base);

        return *this;
    }

protected:

    int a;

};


class Derived : public Base
{


protected:

    std::string b;

};

int main()
{

    Base base(2);
    Derived derived(base);

}

The error (g++ main.cpp) is:

main.cpp: In function ‘int main()’:
main.cpp:71:31: error: no matching function for call to ‘Derived::Derived(Base&)’
     class Derived derived(base);
                               ^
main.cpp:57:7: note: candidate: Derived::Derived()
 class Derived : public Base
       ^~~~~~~
main.cpp:57:7: note:   candidate expects 0 arguments, 1 provided
main.cpp:57:7: note: candidate: Derived::Derived(const Derived&)
main.cpp:57:7: note:   no known conversion for argument 1 from ‘Base’ to ‘const Derived&’
main.cpp:57:7: note: candidate: Derived::Derived(Derived&&)
main.cpp:57:7: note:   no known conversion for argument 1 from ‘Base’ to ‘Derived&&’

So the compiler doesn't know how to convert from an instance of Base to Derived implicitly.

I thought that this should be legal in C++. Do I require an explicit conversion statement?

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • 1
    Why this should be legal in any language? This is the opposite of Liskov substitution principle. – Dan M. Dec 13 '18 at 15:35
  • This is wrong: ` class Base base(2); class Derived derived(base); ` Just use: `Base base(2); Derived derived(base);` – Joseph Franciscus Dec 13 '18 at 15:53
  • 1
    Here's a link. Possible duplicate? https://stackoverflow.com/questions/347358/inheriting-constructors – Joseph Franciscus Dec 13 '18 at 15:58
  • There is no such thing as implicit typecasting. A cast is an explicit type conversion, by definition. – n. m. could be an AI Dec 13 '18 at 16:23
  • Also note that public inheritance without a single virtual function is almost always a mistake, and copying objects that use public inheritance is almost always a mistake too. – n. m. could be an AI Dec 13 '18 at 16:26
  • Please explain what you are trying to achieve without using words class or inheritance. What you are currently describing amonts to the following. You are handed a car of unknown make which you cannot closely inspect, and you are told "Make a copy of this car, and make it a Mercedes". How would you do about it in real life? Assume you have a car building factory at your disposal. I would have no idea. If you can explain that, perhaps we can help you build a software equivalent of this puzzle – n. m. could be an AI Dec 13 '18 at 16:38
  • @JosephFranciscus Sorry that's a typo I'll correct it – FreelanceConsultant Dec 13 '18 at 17:22
  • @n.m. I am extending a library by adding new functions to a class. No I do not want to put them in the base class because they are not general use functions. You are correct that the Mercedes example doesn't make any sense, but I am not writing code to emulate a car. – FreelanceConsultant Dec 13 '18 at 17:25
  • "I am extending a library by adding new functions to a class." This gives just about zero information about existing or intended architecture. "No I do not want to put them in the base class because they are not general use functions". Nobody is expecting you do this, why are you bringing up this fact? "I am not writing code to emulate a car." I don't expect you to emulate a car, I expect you to see an analogy. – n. m. could be an AI Dec 13 '18 at 20:23
  • @n.m. my point being the car analogy isn't appropriate here – FreelanceConsultant Dec 18 '18 at 19:36
  • I respectfully disagree with your assessment of the situation. An analogy is a valid method of explanation, and a car analogy is no better or worse than a mammal analogy or a book analogy or any other kind of analogy. If you think an analogy is not appropriate here, I encourage you to find a better way of explaining what's wrong with your approach. – n. m. could be an AI Dec 18 '18 at 19:49
  • @n.m. A more accurate analogy would be "mercedies" and "mercedies with additional switches on the dash board". But this seems to imply that your analogy would also be appropraite. The reality is neither, really, are. This is a bit pointless for us to discuss further, nobody reading this in future will care about the details as to how applicable one particular analogy would/would not be. – FreelanceConsultant Dec 19 '18 at 15:49
  • It is unlikely that anyone will tell you anything substantially different. If you are still interested in an answer, you may want to ask a different question, explaining your needs in a bit more detail. – n. m. could be an AI Dec 19 '18 at 16:21

5 Answers5

1

What are you doing doesn't make much sense by itself because Base is not a sub-type of Derived so it can't be used as its replacement/substitution, however you could attempt to give it sense (same as with initialization from any other type) by writing a converting constructor:

class Derived : public Base
{
public:
Derived(const Base &bs) : Base(bs), b("constructed from base") {}

protected:

    std::string b;

};

This would first initialize Derived Base part from bs and then init the string b with some value (though you can leave it out if you want it to be default-inited to empty string).

https://godbolt.org/z/GMELW_

Dan M.
  • 3,818
  • 1
  • 23
  • 41
  • This looks like the kind of thing I am after. This will work but just for interest is there any way to write an external function (non member) which will do this conversion. It would be nice to do it somehow such that I don't have to write many versions of the constructors, instead just writing a single conversion function. I may have dreamed this but before making this questionpost I was fairly sure C++ had a way of writing either implicit or explicit conversion functions - now I am not so sure. – FreelanceConsultant Dec 13 '18 at 17:34
  • @user3728501 you can write a function `Derived fromBase(const Base &bs)` that'd do basically the same (but it need to be declared a `friend`) but there is not much sense in it over constructor. Again, you still haven't explained what you really want and why. Why do you think you "have to write many versions of the constructors, instead just writing a single conversion function" ? You have to run a single constructor, as I've shown. The fact that you need it at all already indicates that you are likely doing something wrong. Judging from your other comments, perhaps composition is the key – Dan M. Dec 14 '18 at 14:32
0

Yes, you need to cast explicitely from Base to Derived. Every Mercedes is a car, but not every car is a Mercedes.

Benjamin Bihler
  • 1,612
  • 11
  • 32
  • How should I go about it? – FreelanceConsultant Dec 13 '18 at 15:37
  • @Dan You are right! But the compiler would accept it. ;) I hoped that this would make the first problem more understandable. – Benjamin Bihler Dec 13 '18 at 15:38
  • @user3728501 about what? What is your problem? What are you trying to achieve? Why do you want to init Derived from Base? In general, only the opposite works (because every Derived is Base, not the other way around). For example, Base doesn't have `std::string b` field, so how would that be init-ed? You can write custom conversion constructor if you really need (`Derived(const Base &b) {...}`) but it really seems like an XY problem. – Dan M. Dec 13 '18 at 15:49
  • @DanM. I would like to get my code to compile - using explicit conversion if necessary. At the present time I have no idea what the best way to approach that is, and in typical stackoverflow style there are now at least 3 comments/"answers" telling me that "I am wrong and my code won't compile". Yes I know that - it even says in the question that there was a compilation error. – FreelanceConsultant Dec 13 '18 at 15:56
  • @user3728501 because what you are trying to do doesn't make sense (unless you explain why), and it really looks like you've misunderstood how inheritance works. Again, there a ways to "force" it to compile, but that'd likely just lead to runtime crashes/SIGSEGV. I'll create an answer with the only sensible solution I can think of right now, but it still seems pointless without you explaining what you expect to get. – Dan M. Dec 13 '18 at 16:07
  • @DanM. How would I go about writing code to do that explicit conversion? – FreelanceConsultant Dec 13 '18 at 17:27
0

There is a simple way of solving this problem: By pulling the Base constructor into the scope of Derived.

This can be done with the using statement:

class Derived : public Base
{
public:
    using Base::Base;  // Pulls in the Base class constructors into the scope of Derived

    ...
};
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

this statement Derived derived(base); or to simplify B b(A()); do an implicit conversion of type A to type B, which is legal only if class B inherit directly or indirectly from Class A.

Why ? Because class B could contains new information, in your case a string b, and a cast don't "append" information.

0

I found what I was looking for (I couldn't remember the name so couldn't google it before). However this technique actually doesn't work due to the fact that I am using inheritance. (Or at least I don't know how to make it work.)

Type conversion operator:

Base::operator Derived()
{
    Derived derived;
    derived.a = a;
    return derived;
}

This doesn't actually compile because the compiler doesn't know what Derived is. (Since Derived inherits from Base.) I don't know if it is possible to make this work by separating the compilation units.

FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225