2016-08-21 6 views
45

Есть ли правило, в котором указано, в каком порядке уничтожены члены std :: tuple?C++ std :: tuple порядок уничтожения

Например, если Function1 возвращает std::tuple<std::unique_ptr<ClassA>, std::unique_ptr<ClassB>> к Function2, то можно быть уверенным, что (когда объем Function2 находится слева) экземпляр ClassB, упомянутые во втором элементе разрушается до экземпляра ClassA, на которые ссылаются первый член?

std::tuple< std::unique_ptr<ClassA>, std::unique_ptr<ClassB> > Function1() 
{ 
    std::tuple< std::unique_ptr<ClassA>, std::unique_ptr<ClassB> > garbage; 
    get<0>(garbage).reset(/* ... */); 
    get<1>(garbage).reset(/* ... */); 
    return garbage; 
} 

void Function2() 
{ 
    auto to_be_destroyed = Function1(); 
    // ... do something else 

    // to_be_destroyed leaves scope 
    // Is the instance of ClassB destroyed before the instance of ClassA? 
} 
+2

Я предполагаю, что это в основном зависит от того, как 'std :: tuple' реализован в вашей стандартной библиотеке. – Arunmu

+3

Я не могу найти ничего в спецификации, которая определяет порядок уничтожения для 'std :: tuple'. Вероятно, должно быть подано как неуказанное. – 101010

+1

http://stackoverflow.com/a/27663655/576911 –

ответ

33

В стандарте не указывается порядок разрушения для std::tuple. Тот факт, что §20.4.1/p1 указывает, что:

Конкретизация кортеж с двумя аргументами похожа на конкретизации пары с теми же двумя аргументами.

Подобные здесь не интерпретируется как идентичного и, следовательно, это не подразумевает, что std::tuple должна иметь порядок обратного уничтожения своих аргументов.

Учитывая рекурсивный характер std::tuple, наиболее вероятным является то, что порядок разрушения упорядочен с порядком его аргументов.

Я также основываю свои предположения на отчете об ошибке для GCC BUG 66699, где в обсуждении мои предположения выше оправданы.

Таким образом, порядок уничтожения для std::tuple не указан.

13

С Clang 3.4 я получаю тот же порядок уничтожения для обоих std::pair и 2 элемента std::tuple и с г ++ 5.3 я получаю противоположный порядок, который может быть в основном за счет рекурсивной реализации std::tuple в libstd ++.

Итак, это в основном сводится к тому, что я сказал в комментарии, это реализация определена.

Из отчета о BUG:

Комментарий от Martin Sebor

Поскольку расположение элементов стандов :: паров полностью определен, поэтому является порядком их инициализации и уничтожения. Результат этого теста отражает этот порядок.

Порядок инициализации (и уничтожения) субобъектов std: stup менее четко определен. По крайней мере, это не сразу видно из моего чтения спецификации, если требуется какой-либо конкретный заказ.

Причины, почему выход для станд :: кортежа с libstdC++ обратная из станд :: пара, потому что реализация, которая опирается на рекурсивном наследования, магазины и конструкции кортежа элементов в обратном порядке: т.е. базовый класс, в котором хранится последний элемент, хранится и строится первым, за которым следует каждый производный класс (каждый из которых хранит последний - N-й элемент).

цитата из стандартного [раздела 20.4.1] которой ошибка репортер со ссылкой на

1 В этом подразделе описана библиотека кортежа, который обеспечивает кортеж типа в качестве шаблона класса кортежа, который может быть экземпляр с любым количеством аргументов . Каждый аргумент шаблона указывает тип элемента в кортеже. Следовательно, кортежи неоднородны, коллекции значений фиксированного размера. Репликация кортежа с двумя аргументами похожа на экземпляр пары с теми же двумя аргументами . См. 20.3.

Аргумент против этого сделано в связанном баге:

описывается как похоже, не означает, что они идентичны во всех деталях. std :: pair и std :: tuple - это разные классы с различными требованиями к каждому.Если вы считаете, что необходимо вести себя точно в этом отношении (т. Е. Иметь свои подобъекты, определенные в , в том же порядке), вам необходимо указать на определенную формулировку, которую это гарантирует .

+3

Откуда вы знаете, что порядок уничтожения элементов 'std :: pair' находится в обратном порядке? – deepmax

+2

Аналогичный! = Идентификационный. – 101010

+0

@deepmax На основе реализации как libcxx, так и libstd ++ для std :: pair. – Arunmu

52

Я предложу жизненный урок я узнал, а не прямой ответ, в ответ на ваш вопрос:

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

В контексте кортежей - пожалуйста, будьте любезны для людей, поддерживающих ваш код, и не позволяйте порядку уничтожения элементов кортежа потенциально испортить уничтожение других элементов. Это просто зло ... представьте себе несчастного программиста, которому нужно будет отлаживать эту вещь. Фактически, эта бедная душа может быть собой через несколько лет, когда вы уже забыли о своем умном уловке извне в день.

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

+1

Это действительно важный урок! При программировании на C++ этот совет всегда должен быть в сознании разработчиков. – hbobenicio

+0

Я не совсем понимаю это. Поскольку строки C++ 11 должны иметь непрерывное хранилище. Но есть разумный аргумент (тот, который использовался при написании C++ 03), чтобы не требовать этого. Или, если вы оспариваете этот пример, выберите другое, где стандарт вынес решение, которое может разумно пойти в любом случае. Так не следует ли мне писать код, основанный на том, что C++ 11 или C++ 14 затягивается? Или вы имеете в виду, что если стандарт не ясен, то не полагайтесь на следующего программиста, чтобы иметь волосы сплетения так же, как и вы? –

+0

Или для намеренно глупых примеров есть разумный аргумент, что 'std :: vector' следует называть' std :: dynamic_array', а 'std :: valarray' следует называть' std :: vector'. Но я думаю, что очень разумно писать код на том основании, что 'std :: vector' - это то, что стандарт говорит, что это так: мы не можем использовать его, не предполагая этого. Поэтому я не думаю, что я понял, к каким ситуациям относятся ваши советы ;-) –

Смежные вопросы