среда, 29 февраля 2012 г.

Автогенерируемый конструктор копирования в С++

Пост из серии Holy Standard. Нашел интересную деталь по поводу конструктора копирования (параграф 12.8). Каким его генерирует компилятор? Какую сигнатуру он может иметь?

Допустим, мы имеем класс X, какие сигнатуры могут быть приемлимы в качестве пользовательского конструктора копирования:

X(X&);
X(const X&);
X(const X&, int = 1);
X(const X&, int);

На самом деле все, кроме последнего. По этому поводу стандарт говорит примерно следующее:
«конструктором копирования является нешаблонный конструктор, который принимает в качестве первого аргумента X& , const X&, volatile X& или const volatile X&
и больше не имеет параметров или все остальные параметры имеют дефолтные значения.»
Если не объявлен пользовательский конструктор копирования, то компилятор генерирует его неявно. Вопрос - какую сигнатуру тогда будет иметь неявный конструктор копирования? Ответим на этот вопрос немного позже, а для начала посмотрим на этот код:

struct A {
  A() {};
  A(A&) {};
};

struct B : public A {
  B() {};
};


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

Он скомпилируется? Проверял на своем gcc 4.6.1не собрался. И это правильно, такое поведение задается стандартом. Вот ошибка, которую мне отдал компилятор:

non_const_copy_ctor.cpp:13:10: ошибка: нет подходящей функции для вызова «B::B(const B&)»

Почему так происходит? Вернемся к начальному вопросу - как выглядит конструктор копирования, сгенерированный неявно. Опять же, цитирую стандарт:
«Неявно декларированный конструктор копирования для класса X имеет форму X::X (const X&) в том случае, если:
- каждый прямой или виртуальный базовый класс B класса X имеет копирующий конструктор, чей первый параметр - const B& или const volatile B& , и
- у каждого нестатического члена класса X, который имеет тип M (или массив таковых) имеет конструктор копирования первый аргументом которого является const M& или const volatile M&.
Во всех остальных случаях он имеет форму X::X (X&). Конструктор копирования всегда является inline public членом класса»
Вот так вот. В нашем случае конструктор копирования базового класса принимает неконстантную ссылку, поэтому компилятор для производного класса сгенерировал конструктор с неконстантной ссылкой, отсюда и произошла ошибка.

Комментариев нет:

Отправить комментарий