Реализация предоставленного конструктора копирования и оператора присваивания - PullRequest
2 голосов
/ 02 декабря 2010

У меня небольшая путаница в отношении ситуаций, когда реализация (компилятор) не будет предоставлять конструктор копирования и оператор присваивания копии.

  1. Когда мы объявляем копию ctor и / или оператор присваивания копирования в нашем классе.
  2. Кто-то говорит, когда мы наследуем класс, у которого есть частная копия ctor и / или оператор присваивания копии.

Меня немного смущает вторая ситуация, точно ли вторая ситуация.
a) Реализация не объявит их для вас, поэтому вы получите ошибку времени компиляции.
OR
b) Реализация объявит и определит их, но когда определенная компилятором реализация попытается найти метод базового класса, мы получим ошибку времени компиляции.

У меня было интервью вчера, я сказал, что это (б), что происходит, но интервьюер не согласен, он говорит, что это (а).

Я попытался скомпилировать следующий код в Microsoft C / C ++ 14.00 и gcc 4.4.5

struct A
{
private:
  A& operator = ( const A& );
};

struct B : A
{
};


int main()
{
  B b1;
  B b2;
  b1 = b2;

  return 0;
}

Вывод компилятора Microsoft

ctor01.cpp(9) : error C2248: 'A::operator =' : cannot access private member declared in class 'A'
ctor01.cpp(4) : see declaration of 'A::operator ='
ctor01.cpp(2) : see declaration of 'A'
This diagnostic occurred in the compiler generated function 'B &B::operator =(const B &)'

вывод компилятора gcc

Ctor01.cpp: In member function ‘B& B::operator=(const B&)’:
Ctor01.cpp:4: error: ‘A& A::operator=(const A&)’ is private
Ctor01.cpp:8: error: within this context
Ctor01.cpp: In function ‘int main()’:
Ctor01.cpp:15: note: synthesized method ‘B& B::operator=(const B&)’ first required here 

Так что я думаю, что реализация объявит и определит его, но когда реализация, определенная компилятором, попытается найти метод базового класса, мы получим ошибку времени компиляции. Поправь меня, если я ошибаюсь.

Ответы [ 6 ]

7 голосов
/ 02 декабря 2010

Относительно конструктора копирования, это то, что стандарт говорит (12.8 / 7):

Программа плохо информирована, если класс для которого конструктор копирования неявно определенный имеет:

  • нестатический член данных типа класса (или его массив) с недоступная или неоднозначная копия конструктор, или
  • базовый класс с недоступной или неоднозначной копией конструктор .

Относительно оператора назначения копирования (12.8 / 12):

Программа плохо информирована, если класс для которого оператор присваивания копии неявно определен имеет:

  • нестатический элемент данных типа const или
  • нестатический элемент данных ссылочного типа или
  • нестатический член данных типа класса (или его массив) с оператор присваивания недоступной копии, или
  • базовый класс с недоступным оператором назначения копирования.

То, как компилятор сообщает об ошибке, или как она на самом деле попадает в нее, в значительной степени не имеет значения с моей точки зрения.

Тем не менее, я верю, что ответ (b), вероятно, больше правильный: назначение копии базового класса объявлено, и оно недоступно. Производный класс имеет неявно объявленное назначение копирования, которое компилятор попытается определить , если используется , что делает программу некорректной.

3 голосов
/ 02 декабря 2010

Класс будет иметь конструктор копирования и оператор присваивания копии неявно , объявленный , если не существует объявленной пользователем версии того или другого. Это всегда происходит.

Упрощенно, реализация будет неявно определять только если они действительно используются. Если, когда реализация попытается определить их, неявное определение будет некорректно сформировано (например, для назначения копирования класс содержит ссылочный член или член const или для конструктора копирования у базы или члена есть частный конструктор копирования), тогда программа плохо сформирован.

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

2 голосов
/ 02 декабря 2010

Ваш случай (б) является более точным.

C ++ 03 Стандарт 12.8p10

Если определение класса явно не объявляет оператор присваивания копии, он объявляется неявно .

И 12,8p12

Неявно объявленный оператор присваивания копии неявно определяется , когда объекту его типа класса присваивается значение его типа класса или значение типа класса, производное от его типа класса. Программа плохо сформирована, если класс, для которого неявно определен оператор присваивания копии, имеет:

  • нестатический элемент данных типа const или
  • нестатический элемент данных ссылочного типа или
  • нестатический член данных типа класса (или его массив) с оператором недоступного копирования или
  • базовый класс с недоступным оператором присваивания копии.

Соответствующие требования для неявно определенных конструкторов копирования, конструкторов по умолчанию и деструкторов имеют аналогичные формулировки.

Указание, что методы существуют, даже если их определения будут недопустимыми, проясняет некоторые аспекты разрешения перегрузки. Например,

class A {
private:
  A& operator=(const A&);
};

class B : public A {
public:
  operator int() const;
  B& operator=(int);
};

void f(B& b1, const B& b2)
{ b1 = b2; }

недопустимо, потому что неявно объявленная B::operator=(const B&) является лучшей перегрузкой, но неявное определение неверно сформировано. Без этого объявления вы можете подумать, что компилятор должен неявно преобразовать b2 в int, а затем присвоить его b1.

1 голос
/ 02 декабря 2010

Я думаю, что различие между ними зависит от деталей вашей конкретной реализации (и не имеет большого значения).Для чего это стоит, Comeau дает следующее:

"ComeauTest.c", line 7: error: "A &A::operator=(const A &)" (declared at line 4) is
          inaccessible
  struct B : A
             ^
          detected during implicit generation of "B &B::operator=(const B &)"
                    at line 16

1 error detected in the compilation of "ComeauTest.c".

Так что на этом компиляторе он обнаруживает ошибку «во время» неявного генерирования оператора присваивания B.Другими словами, он пытается его сгенерировать и находит, что не может.На самом деле, не имеет значения, обнаруживает ли он это во время записи или смотрит на A.

0 голосов
/ 02 декабря 2010

Стандарт, похоже, согласен с вами.Цитирование из текущего черновика:

§12.8 / 8:

Если определение класса явно не объявляет конструктор копирования, и не существует объявленного пользователем конструктора перемещения, конструктор копированиянеявно объявляется как значение по умолчанию (8.4).

§12.8 / 12:

Конструктор по умолчанию для копирования / перемещения для класса X определяется как удаленный (8.4.3) если X имеет: […]

  • прямой или виртуальный базовый класс B, который не может быть скопирован / перемещен, поскольку разрешение перегрузки (13.3) применительно к соответствующему конструктору B приводит к неоднозначности илифункция, которая удалена или недоступна из конструктора по умолчанию […]

Итак, конструктор синтезированной копии объявлен и определен, но определен как удаленный.

0 голосов
/ 02 декабря 2010

Вот что происходит:

struct A
{
private:
  A& operator = ( const A& );
};

struct B : A
{
  B& operator = ( const B& other )
  {
    A::operator=( other );
    return *this;
  }
};


int main()
{
  B b1;
  B b2;
  b1 = b2;

  return 0;
}

Оператор по умолчанию = пытается вызвать A :: operator =, который является личным.

...