Проблема при компиляции libstdc ++ с помощью clang - PullRequest
1 голос
/ 14 октября 2019

Я заметил проблему при использовании clang для компиляции libstdc ++ (т. Е. Реализация стандартной библиотеки C ++ в GNU). Вопрос в том, при условии, что проблема подтверждена, кому я должен сообщить об этом?

Это происходит при назначении шага std::vector<X, A<X>> и следующие условия:

  1. A<X> не распространяетсяпри перемещении-назначении и распределителях, используемых исходными и целевыми векторами, не сравнивайте равные.

A MCVE следует (см. live ). К сожалению, он содержит небольшую часть базовой таблицы, и самые важные части обозначены комментариями.

#include <vector>
#include <memory>
#include <type_traits>

struct X {
    X() = default;
    X(const X&) = default;

    // Move constructor might throw
    X(X&&) noexcept(false) = default;

    // Track calls to assignment functions
    X& operator=(const X&) {
        putchar('c'); return *this;
    }
    X& operator=(X&&) noexcept(true) {
        putchar('m'); return *this;
    }
};

unsigned counter = 0;

template <typename T>
struct A : std::allocator<T> {

    template <typename U>
    struct rebind { using other = A<U>; };

    A() : std::allocator<T>(), id(++counter) {}

    // Does not propagate
    using propagate_on_container_move_assignment = std::false_type;

    // Does not always compare equal
    using is_always_equal = std::false_type;
    bool operator ==(const A& o) { return id == o.id; }
    bool operator !=(const A& o) { return id != o.id; }

    unsigned id;
};

int main() {
    std::vector<X, A<X>> a(2), rv(2);
    a = std::move(rv);
}

Насколько я знаю, clang ++ использует libstdc ++ по умолчанию, а использование libc ++ является обязательным для -stdlib=libc++,Выполнение приведенного выше кода (опять-таки, построенного с помощью clang и libstdc ++) отображает cc, что означает, что два элемента rv назначены для копирования в a.

Однако, цитирование [container.requirements.general] / 4, таблица 83

«Все существующие элементы объекта либо перемещены, либо назначены, либо уничтожены» *

(Это дополнительно подтверждается [container.requirements.general] / 16, таблица 86 .)

С другой стороны, переключив компилятор на gcc или библиотекув libc ++ мы получаем mm, что соответствует приведенной выше цитате. Это также происходит путем изменения спецификации исключения X(X&&) на noexcept(true), тогда как спецификация operator =(X&&) кажется неактуальной.

Я что-то упустил? Если нет, кому я должен сообщить о проблеме? Это либо libstdc ++, либо clang. (Я думаю, что это может быть неочевидно.) Я склонен думать, что это последнее, поскольку, AFAIK, clang должен поддерживать libstdc ++, а не наоборот.

( Примечание :приведенный выше код может выявить, по крайней мере, еще одну проблему, а именно, удаление rebind из A приводит к невозможности компиляции clang / libstdc ++, в то время как clang / libc ++ завершается успешно. Мне кажется, что в этом случае вина переворачивается и ложится на libstdc ++, ноэто не часть моего вопроса.)

1 Ответ

1 голос
/ 15 октября 2019

libstdc ++ использует move_if_noexcept для этого , что, вероятно, не следует. Отчеты об ошибках должны идти к ним.

Остальное, по-видимому, связано с расхождением в том, как обрабатывать дефолтную функцию-член с несоответствием спецификации исключения. Это было неправильно сформировано, затем удалено, затем действительно. См. P1286R2 для истории.

  • Clang считает «неправильно сформированным», а затем «действительным», поэтому для версий, в которых он компилируется, move_if_noexcept попытается скопировать.
  • GCC считает "плохо сформированным", а затем "удаленным". Но удаленный конструктор перемещения, который определен как дефолтный, игнорируется разрешением перегрузки, поэтому конструкция перемещения возвращается к использованию дефолтного конструктора copy , который неявно noexcept. В результате move_if_noexcept будет думать, что тип не является ходом, конструктивен и попытается переместиться.
...