Странное поведение инициализации копирования, не вызывает конструктор копирования! - PullRequest
8 голосов
/ 28 мая 2011

Я читал разницу между прямой инициализацией и инициализацией копирования (§8.5 / 12):

T x(a);  //direct-initialization
T y = a; //copy-initialization

Что я понимаю из чтения copy-initialization , так это то, что ему нужен доступный и неявный конструктор копирования , иначе программа не будет компилироваться. Я подтвердил это, написав следующий код:

struct A
{
   int i;
       A(int i) : i(i) { std::cout << " A(int i)" << std::endl; }
   private:
       A(const A &a)  {  std::cout << " A(const A &)" << std::endl; }
};

int main() {
        A a = 10; //error - copy-ctor is private!
}

GCC выдает ошибку ( ideone ), говоря:

prog.cpp: 8: ошибка: «A :: A (const A &)» является приватной

Пока все в порядке, подтверждение того, что говорит Херб Саттер ,

Инициализация копирования означает, что объект инициализируется с использованием конструктора копирования , после первого вызова пользовательского преобразования, если это необходимо, и эквивалентен форме "T t = u;":


После этого я сделал copy-ctor доступным, комментируя ключевое слово private. Теперь, естественно, я ожидал, что будет напечатано следующее:

A (const A &)

Но, к моему удивлению, он печатает это вместо ( ideone ):

A (int i)

Почему?

Хорошо, я понимаю, что сначала создается временный объект типа A из 10 типа int с использованием A(int i), применяя правило преобразования по мере необходимости здесь (§8.5 / 14 ), а затем предполагалось вызвать copy-ctor для инициализации a. Но это не так. Зачем?

Если реализация позволяет устранить необходимость вызывать конструктор копирования (§8.5 / 14), то почему она не принимает код, когда конструктор копирования объявлен private? В конце концов, это не так. Это похоже на избалованного ребенка, который сначала раздраженно просит специфическую игрушку, а когда вы даете ему одну, специфическую игрушку, он выбрасывает ее за спину. : |

Может ли это поведение быть опасным? Я имею в виду, я мог бы сделать что-то еще полезное в copy-ctor, но если оно не вызывает его, то не изменит ли оно поведение программы?

Ответы [ 6 ]

10 голосов
/ 28 мая 2011

Вы спрашиваете, почему компилятор проверяет доступ? 12,8 / 14 в C ++ 03:

Программа некорректна, если копия конструктор или назначение копирования оператор для объекта неявно используется и специальная функция-член недоступен

Когда реализация «опускает конструкцию копирования» (разрешено 12.8 / 15), я не верю, что это означает, что ctor копии больше не «неявно используется», она просто не выполняется.

Или вы спрашиваете, почему стандарт так говорит? Если исключение копирования было исключением из этого правила о проверке доступа, ваша программа была бы правильно сформирована в реализациях, которые успешно выполняют удаление, но плохо сформировалась в реализациях, которые этого не делают.

Я почти уверен, что авторы сочли бы это плохим. Конечно, проще написать переносимый код таким способом - компилятор сообщит вам, если вы напишите код, который пытается скопировать не копируемый объект, даже если копия окажется в вашей реализации. Я подозреваю, что разработчикам также может быть неудобно выяснять, будет ли оптимизация успешной до проверки доступа (или отложить проверку доступа до попытки оптимизации), хотя я понятия не имею, заслуживает ли это внимания рассмотрение .

Может ли это поведение быть опасным? я значит, я мог бы сделать несколько других полезных вещь в copy-ctor, но если это не называет это, тогда не делает это изменить поведение программы?

Конечно, это может быть опасно - побочные эффекты в конструкторах копирования возникают тогда и только тогда, когда объект действительно копируется, и вы должны разработать их соответствующим образом: стандарт гласит, что копии могут быть исключены, поэтому не помещайте код в копируйте конструктор, если вы не рады, что его исключили при условиях, определенных в 12.8 / 15:

MyObject(const MyObject &other) {
    std::cout << "copy " << (void*)(&other) << " to " << (void*)this << "\n"; // OK
    std::cout << "object returned from function\n"; // dangerous: if the copy is
      // elided then an object will be returned but you won't see the message.
}
5 голосов
/ 28 мая 2011

C ++ явно разрешает несколько оптимизаций с использованием конструктора копирования, которые фактически изменяют семантику программы. (Это в отличие от большинства оптимизаций, которые не влияют на семантику программы). В частности, есть несколько случаев, когда компилятору разрешается повторно использовать существующий объект, а не копировать его, если он знает, что существующий объект станет недоступным. Это (копирование конструкции) является одним из таких случаев; другой подобный случай - «оптимизация возвращаемого значения» (RVO), где, если вы объявляете переменную, которая содержит возвращаемое значение функции, тогда C ++ может решить выделить это в кадре вызывающей стороны, так что это не нужно чтобы скопировать его обратно вызывающей стороне после завершения функции.

Как правило, в C ++ вы играете с огнем, если определяете конструктор копирования, который имеет побочные эффекты или выполняет что-то кроме простого копирования.

4 голосов
/ 28 мая 2011

В любом компиляторе процесс анализа синтаксиса [и семантики] выполняется до процесса оптимизации кода.

Код должен быть синтаксически действительным, иначе он даже не будет компилироваться. Только на более позднем этапе (т. Е. Оптимизации кода) компилятор решает исключить созданное им временное хранилище.

Итак, вам нужен доступный экземпляр c-tor.

1 голос
/ 28 мая 2011

Здесь вы можете найти это (с вашим комментарием;)):

[стандарт] также говорит, что временная копия может быть исключено, но семантическая ограничения (например, доступность) конструктор копирования еще должен быть проверено.

0 голосов
/ 28 мая 2011

Это оптимизация компилятором.

При оценке: A a = 10; вместо:

  1. первое построение временного объекта через A(int);

  2. построение a через конструктор копирования и передача во временный;

компилятор просто создаст a, используя A(int).

0 голосов
/ 28 мая 2011

РВО и НРВО, дружище. Совершенно удачный случай копирования ellision.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...