How can I extend a compiler generated copy constructor

后端 未结 5 811
梦如初夏
梦如初夏 2020-12-17 09:41

I frequently run into the problem, that I must extend a compiler generated copy constructor. Example:

class xyz;
class C
{
    ...
    int a, b, c; 
    std:         


        
相关标签:
5条回答
  • 2020-12-17 10:06

    The easiest way is to introduce a base class:

    class xyz;
    
    struct CDetail {
      //...
      int a, b, c; 
      std::set<int> mySet;
      xyz *some_private_ptr;
    };
    
    struct C : private CDetail {
      C(C const &other)
      : CDetail(other)
      {
        if (!CanCopy(other.some_private_ptr))
          some_private_ptr = 0;
        // opposite case already handled
      }
    };
    

    This is an abuse of inheritance to an extent, but the advantages over a nested "impl" class are 1) you can access each member as "name" rather than "data.name" (reducing code changes when refactoring), and 2) (though only sometimes desired) you can "promote" individual members to protected or public without affecting other members:

    struct C : private CDetail {
    protected:
      using CDetail::a;
    };
    
    struct D : C {
      void f() {
        cout << a;
      }
    };
    
    int main() {
      D d;
      d.f();  // D can access 'a'
      cout << d.a;  // main cannot access 'a'
      return 0;
    }
    
    0 讨论(0)
  • 2020-12-17 10:10

    I'd say create a smart pointer that handles the copying, and then use it as a member of your class. These codes may give you an idea:

    Depending on how the base call constructor is initiated, the member's constructors will be called the same way. For example, let's start with:

    struct ABC{
        int a;
        ABC() : a(0)    {   printf("Default Constructor Called %d\n", a);   };
    
        ABC(ABC  & other )  
        {
            a=other.a;
            printf("Copy constructor Called %d \n" , a ) ;
        };
    };
    
    struct ABCDaddy{
        ABC abcchild;
    };
    

    You can do these tests:

    printf("\n\nTest two, where ABC is a member of another structure\n" );
    ABCDaddy aD;
    aD.abcchild.a=2;
    
    printf( "\n Test: ABCDaddy bD=aD;  \n" );
    ABCDaddy bD=aD; // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)
    
    printf( "\n Test: ABCDaddy cD(aD); \n" );
    ABCDaddy cD(aD);    // Does call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is  called)
    
    printf( "\n Test: ABCDaddy eD; eD=aD;  \n" );
    ABCDaddy eD;
    eD=aD;          // Does NOT call the copy constructor of the members of the structure ABCDaddy ( ie. the copy constructor of ABC is not called)
    

    Output:

    Default Constructor Called 0
    
    Test: ABCDaddy bD=aD;
    Copy constructor Called 2
    
    Test: ABCDaddy cD(aD);
    Copy constructor Called 2
    
    Test: ABCDaddy eD; eD=aD;
    Default Constructor Called 0
    

    Enjoy.

    0 讨论(0)
  • 2020-12-17 10:13

    the moment you define your own copy ctor, the compiler does not bother generating one for you. Unfortunately this means you have to do all the leg work yourself! You could group the members into some sort of impl_ structure within your class, and then rely on the copy ctor for that.

    for example:

    class xyz;
    class C
    {
      struct impl_
      {
        int a, b, c; 
        std::set<int> mySet;
        xyz *some_private_ptr;
      };
    
      impl_ data;
    };
    

    now in your copy ctor

    C::C(const C &other) : data(other.data)
    {
     // specific stuff...      
    }
    
    0 讨论(0)
  • 2020-12-17 10:18

    You might place your special member in a base class, something like:

    class xyz;
    
    class SpecialCopyXYZ
    {
    public:
        SpecialCopyXYZ() = default;
        SpecialCopyXYZ(const SpecialCopyXYZ& rhs)
        {
           if (CanCopy(other.some_private_ptr)) {
              some_private_ptr = other.some_private_ptr;
           } else {
              some_private_ptr = nullptr;
           }
        }
    
        // SpecialCopyXYZ& operator=(const SpecialCopyXYZ& rhs)
    
    protected:
        xyz *some_private_ptr = nullptr;
    };
    
    
    class C : private SpecialCopyXYZ
    {
    public:
        C(const C &other) = default;
    private:
        int a, b, c; 
        std::set<int> mySet;
    };
    

    If SpecialCopyXYZ need C data, you may use CRTP and downcast.

    0 讨论(0)
  • 2020-12-17 10:25

    The problem here is your class is trying to do too much. Either use a resource, or manage a resource. You don't do both, ever, because your code will become an unsafe, pile of slop. And that's no good.

    You need to design a class that manages a resource that is only copied under certain conditions. You haven't really expanded on what those conditions on and why they're there in the first place (that's an awfully strange way to "copy" data, are you sure this is the best route?), but it'd be something like this:

    // pointer to your condition member (not sure if this is even needed,
    // is this condition knowable via the pointer alone? you get the point)
    template <typename T, typename D, class Tag = void>
    class copy_conditional_ptr
    {
    public:
        copy_conditional_ptr(bool (D::*condition)(T*) const, T* value = 0) :
        mCondition(condition),
        mValue(value)
        {}
    
        // here's where the unique copy-semantics go
        copy_conditional_ptr(const copy_conditional_ptr& other) :
        mCondition(other.mCondition),
        mValue(do_copy(other.mValue) ? other.mValue : 0)
        {}
    
        // other stuff for a smart pointer,
        // copy-and-swap, etc...
    
    protected:
        // protected because it's meant to be a base class
        ~copy_conditional_ptr()
        {
            // whatever
        }
    
    private:
        bool do_copy(T* value) const
        {
            const D& self = static_cast<const D&>(*this);
            return (self.*mCondition)(other.value);
        }
    
        bool (D::*mCondition)(T*) const;
        T* mValue;
    };
    

    Then you use it like this:

    class xyz;
    
    class C : private copy_conditional_ptr<xyz, C>
    {
    public:
        C() :
        /* others, */
        copy_conditional_ptr(&C::CanCopy)
        {}
    
    private:
        int a, b, c; 
        std::set<int> mySet;
    };
    

    And let the management be automatic for the rest of the class. The tag is so you can have multiple in the same class:

    class C : private copy_conditional_ptr<xyz, C, struct C_first>,
                private copy_conditional_ptr<xyz, C, struct C_second>
    {
        // ...
    };
    
    0 讨论(0)
提交回复
热议问题