Является ли предупреждение «используется после того, как оно было перемещено [bugprone-use-after-move]» настоящей проблемой? - PullRequest
0 голосов
/ 11 января 2019

Я адаптирую идею отложенного ptr, о котором говорил Херб Саттер в cppcon 2016, для более безопасного управления внешними ресурсами, представленными идентификатором.

Поэтому я создал не копируемый и только подвижный класс, содержащий id, который должен представлять ресурс. Как и unique_ptr, id должен стать 0, если объект перемещен в другой объект.

Насколько я понимаю, вам все равно следует разрешить использовать объект даже после его перемещения, если вызываемая функция не имеет каких-либо предварительных условий, поэтому, насколько я понимаю, это должно быть допустимо:

int main() {

    resource src = make_resource(10);
    resource dst;
    std::cout << "src " << src.get() << std::endl;
    std::cout << "dst " << dst.get() << std::endl;

    dst = std::move(src);
    std::cout << "src " << src.get() << std::endl; // (*)
    std::cout << "dst " << dst.get() << std::endl;

    src = make_resource(40);
    std::cout << "src " << src.get() << std::endl;
    std::cout << "dst " << dst.get() << std::endl;

    return 0;
}

Но clang-tidy дай мне это предупреждение:

предупреждение: 'src' используется после его перемещения [bugprone-use-after-move]

Для src.get() после dst = std::move(src) (отмечено выше).

Итак, мои вопросы:

  1. Могу ли я позвонить src.get() после std::move(src)
  2. Могу ли я сделать предположение, что src.get() возвращает 0 после std::move.
  3. Если 1. и 2. действительны, то есть способ изменить код так, чтобы clan-tidy знал, что это верно. А если нет, то есть ли способ изменить код, чтобы он действовал?

Вот реализация класса:

struct resource {
    resource() = default;

    // no two objects are allowed to have the same id (prevent double free, only 0 is allowed multiple times as it represents nullptr)
    resource(const resource&) = delete;
    resource& operator=(const resource& other) = delete;

    // set the id of the object we move from back to 0 (prevent double free)
    resource(resource&& other) noexcept : id(std::exchange(other.id, 0)) {}
    resource& operator=(resource&& other) noexcept {
        id = std::exchange(other.id, 0);
        return *this;
    }

    // will free the external resource if id not 0
    ~resource() = default;

    // returns the id representing the external resource
    int get() const noexcept { return id; }

  protected:
    // only allow the make function to call the constructor with an id
    friend resource make_resource(int id);
    explicit resource(int id) : id(id) {}

  protected:
    int id = 0; // 0 = no resource referenced
};

// in the final version the id should be retrieved by from the external ip
resource make_resource(int id) { return std::move(resource(id)); }

Ответы [ 2 ]

0 голосов
/ 11 января 2019
  1. Могу ли я вызвать src.get () после std :: move (src)

Если мы остаемся агностиком в отношении типа src, тогда мы не знаем. Потенциально нет. Есть случаи, когда вызов функции-члена после перемещения будет неопределенным. Например, вызывая оператор косвенности умного указателя.

Учитывая определение decltype(src) и его функций-членов, которые вы показали, мы знаем: да, вам разрешено.

  1. Могу ли я предположить, что src.get () возвращает 0 после std :: move.

Если мы остаемся агностиком в отношении типа src, мы ничего не знаем о том, что делает src.get(). Точнее, мы не знаем его предпосылок.

Учитывая приведенное вами определение decltype(src) и его функций-членов: Да, мы можем сделать предположение.

  1. Если 1. и 2. действительны, то есть способ изменить код, чтобы clan-tidy знал, что это верно. А если нет, то есть ли способ изменить код, чтобы он действовал?

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

Вы можете удалить вызов на src.get() между переездом и переназначением src. Одна операция над перемещенной переменной, на которую clang-tidy не жалуется, - это переназначение, и после этого присваивания состояние объекта должно (условно) быть хорошо определенным, и вызов других функций-членов считается нормальным (конечно, , у вас могут быть другие предварительные условия, которые должны быть выполнены, но Clang-Tidy, вероятно, не знает о них). Хотя технически можно было бы определить тип, для которого даже назначение после перемещения не является четко определенным, но такой тип был бы весьма нетрадиционным и небезопасным.


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

0 голосов
/ 11 января 2019

cppreference.com имеет этот текст :

Если не указано иное, все стандартные объекты библиотеки, которые имеют были перемещены из находятся в допустимом, но не указанном состоянии. То есть, только функции без предварительных условий, такие как присвоение оператор, может безопасно использоваться на объекте после его перемещения из:

Итак, неофициально, соглашение C ++ состоит в том, что перемещенный объект будет действительным, но бесполезным, поэтому clang-tidy предполагает, что использовать его подозрительно.

Для вашей реализации вы предоставили «больше», чем соглашение & ndash; так что ваш код не неправильный, просто нетрадиционный.

...