Clang изменяет возвращаемое значение в деструкторе? - PullRequest
0 голосов
/ 07 февраля 2019

Пытаясь написать класс, который бы умножил продолжительность между вызовом его конструктора и деструктора, я столкнулся с тем, что я считаю ошибкой в ​​clang.(Правка: это не ошибка; это определенная реализацией копия elision)

Структура timer, приведенная ниже, хранит указатель на объект продолжительности, который передается в качестве ссылки, и добавляет к этому продолжительность области.

#include <iostream>
#include <chrono>
struct timer {
    using clock      = std::chrono::high_resolution_clock;
    using time_point = clock::time_point;
    using duration   = clock::duration;
    duration* d_;
    time_point start_;
    timer(duration &d) : d_(&d), start_(clock::now()) {}
    ~timer(){
        auto duration = clock::now() - start_;
        *d_ += duration;
        std::cerr << "duration: " << duration.count() << std::endl;
    }
};

timer::duration f(){
    timer::duration d{};
    timer _(d);
    std::cerr << "some heavy calculation here" << std::endl;
    return d;
}

int main(){
    std::cout << "function: " << f().count() << std::endl;
}

При компиляции с помощью clang 7.0.0 вывод будет:

some heavy calculation here
duration: 21642
function: 21642

, а для g ++ 8 вывод будет

some heavy calculation here
duration: 89747
function: 0

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

Это ошибка с Clang?Или это зависит от (определенной реализации?) Оптимизации возвращаемого значения?

Поведение одинаково независимо от того, является ли duration d в timer указателем или ссылкой.

--

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

timer::duration f(){
    timer::duration d{};
    {
        timer _(d);
        std::cerr << "some heavy calculation here" << std::endl;
    }
    return d;
}

1 Ответ

0 голосов
/ 07 февраля 2019

Краткий ответ: из-за NRVO выходные данные программы могут быть либо 0, либо фактической продолжительностью.Оба действительны.


Для фона, смотрите сначала:

Указание:

  • Избегайте деструкторов, изменяющих возвратзначения.

Например, когда мы видим следующий шаблон:

T f() {
    T ret;
    A a(ret);   // or similar
    return ret;
}

Мы должны спросить себя: A::~A() изменяет ли наше возвращаемое значение каким-либо образом?Если да, то наша программа, скорее всего, имеет ошибку.

Например:

  • Тип, который печатает возвращаемое значение при уничтожении, в порядке.
  • Типкоторый вычисляет возвращаемое значение при уничтожении не штраф.
...