Подтвердить что ты не робот

Как я могу расширить созданный компилятор конструктор копии

Я часто сталкиваюсь с проблемой, что я должен расширить созданный компилятор конструктор копии. Пример:

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

Предположим, что some_private_ptr следует копировать только при определенных условиях. Для других условий он должен быть reset до NULL на копии. Поэтому мне нужно написать конструктор копирования, например:

C::C(const C &other) : 
     a(other.a), 
     b(other.b), 
     c(other.c), 
    mySet(other.mySet)
{      
   if(CanCopy(other.some_private_ptr)) // matches condition
      some_private_ptr = other.some_private_ptr;
   else
      some_private_ptr = NULL;
}

Проблема в том, что класс может иметь несколько членов данных, и я, скорее всего, не забуду обновить конструктор копирования, когда я добавлю элемент данных. Было бы очень приятно, если бы я просто написал.

C::C(const C &other) :
   C::default_copy(other)
{      
   if(CanCopy(other.some_private_ptr)) // matches condition
      some_private_ptr = other.some_private_ptr;
   else
      some_private_ptr = NULL;
}

Это сделает мой код более безопасным и простым в обслуживании. К сожалению, я не знаю такой возможности. Есть ли?

4b9b3361

Ответ 1

В тот момент, когда вы определяете свою собственную копию ctor, компилятор не беспокоится о том, чтобы создать ее для вас. К сожалению, это означает, что вы должны делать всю работу ноги самостоятельно! Вы могли бы группировать элементы в какую-то структуру impl_ внутри вашего класса, а затем полагаться на копию ctor для этого.

например:

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

  impl_ data;
};

теперь в вашей копии ctor

C::C(const C &other) : data(other.data)
{
 // specific stuff...      
}

Ответ 2

Самый простой способ - ввести базовый класс:

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

Это злоупотребление наследованием до некоторой степени, но преимущества над вложенным классом "impl": 1) вы можете получить доступ к каждому члену как "имя", а не "data.name" (уменьшая изменения кода при рефакторинге) и 2) (хотя только иногда желательно) вы можете "продвигать" отдельных членов в защищенные или общедоступные не затрагивая других участников:

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

Ответ 3

Проблема в том, что ваш класс пытается сделать слишком много. Либо используйте ресурс, либо управляйте ресурсом. Вы не выполняете оба, никогда, потому что ваш код станет небезопасным, кучей провалов. И это нехорошо.

Вам нужно создать класс, который управляет ресурсом, который копируется только при определенных условиях. Вы действительно не расширили свои условия и почему они там в первую очередь (что ужасно странный способ "скопировать" данные, вы уверены, что это лучший маршрут?), Но это было бы что-то например:

// 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 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 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;
};

Затем вы используете его следующим образом:

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

И пусть управление будет автоматическим для остальной части класса. Тег так что вы можете иметь несколько в одном классе:

class C : private copy_conditional_ptr<xyz, C, struct C_first>,
            private copy_conditional_ptr<xyz, C, struct C_second>
{
    // ...
};

Ответ 4

Я бы сказал, создайте умный указатель, который обрабатывает копирование, а затем используйте его как член вашего класса. Эти коды могут дать вам представление:

В зависимости от того, как инициируется конструктор базового вызова, конструкторы-члены будут вызываться одинаково. Например, пусть начнется с:

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

Вы можете выполнить следующие тесты:

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)

Вывод:

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.

Ответ 5

Вы можете разместить свой специальный член в базовом классе, например:

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

Если SpecialCopyXYZ нужны C данные, вы можете использовать CRTP и downcast.