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

#c

#c

Вопрос:

Некоторое время назад у нас было это объявление (упрощенное):

Объявление 1

 struct SomeType
{
  // allocates Implementation in SomeType's TU
  SomeType();
  // ... other stuff
  struct Implementation
  {
     std::string someAtribute1;
     std::string someAtribute2;
  }
  std::auto_ptr< Implementation> pimpl;
};
  

Некоторое время спустя мы изменили объявление de на это (упрощенное):

Объявление 2

 class OtherNameImplementation;

struct SomeType
{
   // allocates OtherNameImplementation in SomeType's TU
  SomeType();
   // ... other stuff
   std::auto_ptr< OtherNameImplementation> pimpl;
};
  

Где OtherNameImplementation определено в SomeType TU и имеет точно такое же определение, как SomeType::Implementation было (в объявлении 1).

Каким-то образом мы пропустили, что SomeType не имеет определенного деструктора, следовательно, деструктор, сгенерированный компилятором, определен в пользовательском TU.

Вопрос:

Для TU, которые были скомпилированы против ‘Declaration 1’, Существует ли какая-либо форма гарантий того, что поведение «правильное», когда во время выполнения оно использует ‘Declaration 2’.

Есть две причины, о которых я могу думать, что это, по крайней мере, кажется «работающим»:

  1. Деструктор, созданный компилятором, должен делать «правильные вещи», поскольку расположение памяти одинаково для обоих типов реализации, даже если имя типа изменилось.

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

Наша целевая платформа — Solaris, и мы используем Sunstudio 12.

Каким-то образом мы пропустили это во время проверки кода, и я знаю, что это, по крайней мере, UB, и в идеальном мире мы определили бы деструктор для SomeType и перекомпилировали бы каждый TU, который зависит от этого.

Обновить:

Я «перекрестно опубликовал» это на comp.lang.c , где Альф П. Штейнбах заставил меня понять, что TU, скомпилированный против ‘Declaration 2’, пропускает память (что довольно неловко). Исходя из этого, мы вернемся к ‘Declaration 1’ и перекомпилируем каждый домен, который скомпилирован по ‘Declaration 2’, следовательно, у нас нет развязки заголовка или ABI 🙂 но мы находимся в допустимом состоянии. Мы исправим это в следующем цикле выпуска.

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

1. Помимо вопроса, который полезно задать, не можете ли вы повторно скомпилировать весь код для нового модифицированного переработанного кода? ИМХО, это очень плохая идея компилировать один фрагмент кода и связывать его с другим.

2. Это могло бы помочь, если бы вы определили TU и UB.

3. @gregg: Оба термина (или, по крайней мере, расширения обоих сокращений, единицы перевода и неопределенного поведения) четко определены стандартом C .

4. @Als Если бы мы могли, приложив небольшие усилия, я бы не задавал этот вопрос 🙂 В зависимости от результата этого, мы вполне можем быть вынуждены это сделать, и наше руководство не будет счастливо…

5. @Tomalak: Вы правы в отношении расширенных условий. Базовая точка связи. Зачем излишне запутывать вопрос?

Ответ №1:

Гарантия наложения, на которую вы ссылаетесь, хороша, если нет базовых / производных классов и виртуальных функций.

Я не думаю, что вам разрешено предполагать, что std::auto_ptr или std::string соответствуют этому.

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

1. Я не совсем понимаю, почему std::auto_ptr или std::string не удовлетворяют этим требованиям. По крайней мере, с той реализацией, которая у нас есть (RogueWave).

2. … кроме того, деструктор для std::auto_ptr (который, вероятно, встроен) просто вызывает деструктор для «pimpl-type» (определенный в пользовательском TU), у которого нет виртуального / базового / sub. -> вызывает деструкторы для каждого атрибута (что в точности соответствует памяти), ИМХО, деструктор std::string должен вызываться правильно, если у нас пока есть гарантии. Или я вас неправильно понимаю?

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

Ответ №2:

Я «перекрестно опубликовал» это на comp.lang.c , где Альф П. Штейнбах заставил меня понять, что TU, скомпилированный против ‘Declaration 2’, пропускает память (что довольно неловко). Исходя из этого, мы вернемся к ‘Declaration 1’ и перекомпилируем каждый домен, который скомпилирован по ‘Declaration 2’, следовательно, у нас нет развязки заголовка или ABI 🙂 но мы находимся в допустимом состоянии. Мы исправим это в следующем цикле выпуска.