Перемещает ли идиоматика (например, thread_enumerable_specific 'TBB) деструктор вызова присваивания на исходный объект - PullRequest
0 голосов
/ 27 марта 2019

Допустим, я работаю с идиоматической библиотекой Cpp (например, Intel TBB) и в некотором классе есть член in-place (например, TsCountersType _ts_counters;). Такой элемент автоматически инициализируется его конструктором по умолчанию (если он существует, в противном случае ошибка компиляции), если только мой собственный конструктор KMeans не инициализирует его явно, вызывая его конструктор напрямую.

Тогда, если я назначу новое (созданное с помощью вызова конструктора) значение в поле члена в обычном неконструкторном методе (например, init), что конкретно можно предположить?

  1. Я знаю, что в этом случае правая часть присваивания равна rvalue, и поэтому будет вызван оператор move assignment.
  2. Я думаю, можно с уверенностью предположить, что идиоматический код Cpp с хорошим поведением должен вызывать деструктор исходных объектов (например, _ts_counters) перед перемещением нового объекта в область памяти. Но разве такое предположение звучит?
  3. А как насчет оператора назначения перемещения по умолчанию? Вызывает ли он деструктор на исходном объекте, прежде чем войти? Это даже уместный вопрос или оператор присваивания перемещения по умолчанию создан компилятором, только если (среди прочих условий) явный деструктор не определен?

    1. Если это так, то что происходит, если у меня есть сценарий, похожий на сценарий TBB, где вместо TsCountersType у меня есть простой настраиваемый тип с одним unique_ptr и все остальное по умолчанию (конструкторы, деструкторы, назначения перемещения,…) , Когда unique_ptr выйдет из области видимости?
  4. В частности, в случае TBB, могу ли я предположить, что это происходит, учитывая их документацию : Supported since C++11. Moves the content of other to *this intact. other is left in an unspecified state, but can be safely destroyed.

Пример кода:

class KMeans 
{
  private:
    //...
    // thread specific counters
    typedef std::pair<std::vector<point_t>, std::vector<std::size_t>> TSCounterType;
    typedef tbb::enumerable_thread_specific<TSCounterType> TsCountersType;
    TsCountersType _ts_counters;

  public:
    //...
    KMeans() : _tbbInit(tbb::task_scheduler_init::automatic) {}

    virtual void init(std::size_t points, std::size_t k, std::size_t iters)
    {
        // When _ts_counters is replaced by a new object the destructor is automatically called on the original object
        _ts_counters = TsCountersType(std::make_pair(std::vector<point_t>(k), std::vector<std::size_t>(k)));

    }
};

1 Ответ

1 голос
/ 27 марта 2019

Рассмотрим этот код:

#include<iostream>

struct S
{
    S() { std::cout << __PRETTY_FUNCTION__ << std::endl;}
    S(S const &) { std::cout << __PRETTY_FUNCTION__ << std::endl;}
    S(S&&) { std::cout << __PRETTY_FUNCTION__ << std::endl;}
    S& operator=(S const &) { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this;}
    S& operator=(S&&) { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this;}
    ~S() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

struct W
{
    W() : s{} {}
    void init() { s = S{}; }
private:
    S s;
};

int main()
{
    W w{};
    w.init();
}

Выходные данные (проверенные clang и gcc):

S::S()
S::S()
S &S::operator=(S &&)
S::~S()
S::~S()

Итак, что именно происходит:

  • W, который по умолчанию инициализирует S
  • Временный S по умолчанию создается и сразу же перемещается из W::s member
  • Временный объект теперь уничтожен (незадолго до выхода из init)
  • W::s разрушен на главном выходе.
...