Неправильное вложение переменных областей в C ++? - PullRequest
2 голосов
/ 20 июля 2011

У меня есть код, который выглядит примерно так:

ComplexObject cpy;
{
  RAIILockObject _(obj->mutex);
  cpy = obj->org;
}
// use cpy

Ради аргумента предположим, что конструктор по умолчанию для ComplexObject является дорогостоящим.

  • Can (и делать) компиляторы C ++ заменяют конструкцию / назначение cpy по умолчанию конструктором копирования?
  • Есть ли способ реструктурировать код так, чтобы заставить эту оптимизацию при сохранении области видимости обоих локальных объектов?

Редактировать: Iдействительно ищу общее решение проблемы нежелательного вложения объектов RAII с другими вещами.

Есть какие-нибудь комментарии по этому поводу решения Конрада Рудольфа ?

ComplexObject = LockedInitInPlace(obj->org, obj->mutex);

template<class C> C LockedInitInPlace(C& c, Mutex& m) {
    RAIILockObject _(m);
    return c;
}

Редактировать 2:

Исходный код имеет следующую последовательность:

  1. Конструкция по умолчанию cpy
  2. Конструкция RAII lock
  3. назначить (скопировать) существующий объект на cpy
  4. уничтожить lock
  5. использовать cpy
  6. уничтожить cpy

То, что я хочу, это:

  1. конструкция RAII lock
  2. конструкция cpy (в данном случае конструктор копирования с использованием существующего объекта).
  3. уничтожить lock
  4. использовать cpy
  5. уничтожить cpy

Ответы [ 3 ]

3 голосов
/ 20 июля 2011

Маловероятно, что конструктор по умолчанию будет исключен, если конструктор будет сложным.

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

Лучший способ решить эту проблему - не делать конструктор по умолчанию ComplexObject дорогим.По этой причине плохие практики иметь дорогие конструкторы по умолчанию.

3 голосов
/ 20 июля 2011

Могут ли (и могут ли) компиляторы C ++ заменить конструкцию / назначение cpy по умолчанию конструктором копирования?

Нет, компилятору запрещено делать это (при условии, что конструктор по умолчанию)вашего класса достаточно сложен, так что компилятор не может доказать, что его исключение приведет к эквивалентной программе).Любой компилятор, который не соответствует стандарту.

РЕДАКТИРОВАТЬ: следующее решение имеет недостатки!Не используйте его!

Следующее решение скрывает состояние гонки. Если ваша блокировка должна была обеспечить копирование в критическом разделе, то мое «решение» нарушит это предположениепоскольку копирование может (и, вероятно, произойдет) произойти за пределами этого критического раздела.Это только работает, если вы выполняете другую работу.Но в исходном коде мьютекс имеет смысл только в том случае, если само копирование является критическим.

Просто сделайте следующее, чтобы предотвратить создание по умолчанию:

ComplexObject = init(any_params_here);

ComplexObject init(any_params_here) {
    RAIILockObject _(obj->mutex);
    return obj->org;
}

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

3 голосов
/ 20 июля 2011

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

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

В качестве альтернативы вам придется использовать кучу и указатели (посредством создания конструкции копирования), а не локальные экземпляры.

std::scoped_ptr<ComplexObject> cpyPtr = 0;
{
  RIAALockObject _(obj->mutex);
  cpyPtr = new ComplexObject(obj->org);
}
ComplexObject& cpy = *cpyPtr;  // create alias for ease of use.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...