使用clang

时间:2019-10-14 13:39:17

标签: c++ c++17 clang++ libstdc++

当使用clang编译libstdc ++时(即GNU对C ++标准库的实现),我注意到一个问题。 问题是,如果问题得到确认,我应该向谁报告?

在移动分配std::vector<X, A<X>>时发生,并且条件是:

  1. X的移动构造函数可能会抛出(尽管我是 assigning 而不是在构造);

  2. A<X>不会在移动分配上传播,并且源向量和目标向量使用的分配器的比较不相等。

后接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的两个元素被 copy-assigned 复制到a中。

但是,引用[container.requirements.general]/4, Table 83

  
    

“ a的所有现有元素都已被移动分配或销毁了”

  

(这进一步得到了 [container.requirements.general]/16, Table 86。)

另一方面,通过将编译器切换为gcc或将库切换为libc ++,我们得到mm,它与上面的引用一致。通过将X(X&&)的异常规范更改为noexcept(true)也会发生这种情况,而operator =(X&&)的异常规范似乎无关紧要。

我想念什么吗?如果没有,我应该向谁报告该问题?它是libstdc ++或clang。 (我认为这可能并不明显。)我倾向于认为是后者,因为AFAIK clang应该支持libstdc ++,而不是相反。

附带说明:上面的代码至少可以揭示另一个问题,即从rebind中删除A会使clang / libstdc ++无法编译,而clang / libc ++可以成功。在我看来,在这种情况下,责任应归咎于libstdc ++,但这不是我的问题。)

1 个答案:

答案 0 :(得分:1)

libstdc ++正在使用move_if_noexcept for this,这可能不应该。错误报告应该交给他们。

其余似乎是由于在处理异常规范不匹配的默认成员函数方面存在分歧。这是格式错误,然后删除,然后有效。有关历史记录,请参见P1286R2

  • Clang认为“格式错误”然后“有效”,因此对于其编译版本,move_if_noexcept将尝试复制。
  • GCC认为“格式错误”,然后“删除”。但是,重载解析会忽略已定义为默认值的已删除的move构造函数,因此,重载构造将回退到使用默认的 copy 构造函数,该构造函数隐含了noexcept。结果,move_if_noexcept会认为该类型不是可移动构造的,而是尝试移动。