Cast to a Child

前端 未结 4 1747
无人及你
无人及你 2021-01-23 07:47

What I\'m actually trying to do is cast a constructed moneypunct to the punct_facet in this question without writing a copy constructor as in this answ

4条回答
  •  北海茫月
    2021-01-23 08:19

    So this is implementation specific as Wikipedia notes:

    The C++ standards do not mandate exactly how dynamic dispatch must be implemented, but compilers generally use minor variations on the same basic model.

    Typically, the compiler creates a separate vtable for each class. When an object is created, a pointer to this vtable, called the virtual table pointer, vpointer or VPTR, is added as a hidden member of this object. The compiler also generates "hidden" code in the constructor of each class to initialize the vpointers of its objects to the address of the corresponding vtable.

    Even worse Danny Kalev states:

    Compilers can be divided into two categories with respect to their vptr's position. UNIX compilers typically place the vptr after the last user-declared data member, whereas Windows compilers place it as the first data member of the object, before any user-declared data members.

    I give all the preceding information to indicate the conditions in which my hack will work:

    1. is_standard_layout fails for your child because it has: "has virtual functions or virtual base classes"
    2. Your parent does not have an copy constructor or an assignment operator (otherwise you could just copy in the parent on construction of the child object)
    3. Your compiler does in fact implement a v-table
    4. Whether your v-table is at the beginning or end of the object layout
    5. The position your compiler assigns to child member variables relative to parent member variables in the object layout

    With understanding of the hack that you are getting into I will now proceed to extend the classes in the question to better demonstrate how we can "Cast to a Child":

    class Parent{
    public:
        Parent operator=(const Parent&) = delete;
        Parent(const Parent&) = delete;
        Parent() = default;
        Parent(int complex) : test(complex) {}
    
        virtual void func(){ cout << test; }
    private:
        int test = 0;
    };
    
    class Child : public Parent{
    public:
        Child operator=(const Child&) = delete;
        Child(const Child&) = delete;
        Child() = default;
        Child(const Parent* parent){
            const auto vTablePtrSize = sizeof(void*);
    
            memcpy(reinterpret_cast(this) + vTablePtrSize,
                   reinterpret_cast(parent) + vTablePtrSize,
                   sizeof(Parent) - vTablePtrSize);
        }
    
        virtual void func(){ cout << "Child, parent says: "; Parent::func(); }
    };
    

    We are simply using memcpy to copying the state of the parent object, while allowing all Child information to persist.

    You can see that this code:

    Parent foo(13);
    Child bar(&foo);
    
    bar.func();
    

    Will print:

    Child, parent says: 13

    Live example and though it's not asked for in the question here's how it could be accomplished for multiple inheritance: http://ideone.com/1QOrMz

    This is a useful solution for moneypunct as it's initialization will be implementation specific anyway, since C++ does not specify any locale names other than:

    • ""
    • "C"
    • "POSIX"

    I would like to close this answer by pointing out that this entire post has been about how to overcome limitations intentionally put in place by the designers of moneypunct. So, yes you can do this but as you do the obvious question should be asked: "Why were the copy constructor and assignment operator of moneypunct deleted in the first place? What aspect of that design am I intentionally subverting?"

提交回复
热议问题