"pair::operator=(pair&&)"错误,"auto&"推断出移动操作 - libstdc++ 回归?

`pair::operator=(pair&&)` error with `auto&` deduced move operations - libstdc++ regression?

本文关键字:pair 移动 操作 libstdc++ 回归 推断出 错误 operator auto      更新时间:2023-10-16

给定此程序:

struct Val
{
Val() = default;
Val(Val&&) = default;
auto& operator=(Val&&);
};
/* PLACEHOLDER */
auto& Val::operator=(Val&&) { return *this; }   

用…替换/* PLACEHOLDER */。。。

int main()
{
std::vector<std::pair<int, Val>> v;
v.emplace(std::begin(v), 0, Val{});
}

成功编译于:

  • g++6.2.0
  • g++6.3.0
  • g++7.0.1(主干)

  • 叮当++3.9.1

  • 叮当++5.0.0(头部)

在wandbox


用…代替/* PLACEHOLDER */。。。

template <typename TVec>
void a(TVec& v)
{
v.emplace(std::begin(v), 0, Val{});
}
int main()
{
std::vector<std::pair<int, Val>> v;
a(v);
}

成功编译于:

  • g++6.2.0
  • 叮当++3.9.1

。。。但是在上产生编译时错误

  • g++6.3.0
  • g++7.0.1(主干)
  • 叮当++5.0.0(头部)

在wandbox


产生的错误似乎与受约束的pair operator=(pair&&)过载有关——来自GitHub的libstdc++镜像上的include/bits/stl_pair.h

pair&
operator=(typename conditional<
__and_<is_move_assignable<_T1>,
is_move_assignable<_T2>>::value,
pair&&, __nonesuch&&>::type __p)
noexcept(__and_<is_nothrow_move_assignable<_T1>,
is_nothrow_move_assignable<_T2>>::value)
{
first = std::forward<first_type>(__p.first);
second = std::forward<second_type>(__p.second);
return *this;
}
std::true_type替换is_move_assignable<_T2>可以编译代码。
  • Val::operator=(Val&&)的定义移到/* PLACEHOLDER */之前可以编译代码。

  • auto& Val::operator=(Val&&)更改为Val& Val::operator=(Val&&)可以编译代码。

  • 这里发生了什么?这是最新版本的libstdc++中的实现缺陷吗?还是旧版本错误地编译了格式错误的代码


    EDIT:正如AndyG在他的(现已删除)应答中发现的那样,在调用emplace:之前调用空函数时也会发生错误

    template <typename TVec>
    void a(TVec&) { }
    int main()
    {
    std::vector<std::pair<int, Val>> v;
    a(v);
    v.emplace(std::begin(v), 0, Val{});
    }
    

    用上面的a(v);进行通信可以防止产生编译时错误这种行为在g++7和clang++5中都存在。

    在wandbox上


    另一个奇怪的案例是由Sergey Murzin发现的,可以在wandbox上进行测试

    int main()
    {
    std::vector<std::pair<int, Val>> v;
    v.emplace(v.begin(), 0, Val{});
    std::cout << v.back().first << std::endl;
    }
    

    上面的代码会产生编译器错误。注释掉包含std::cout的行可以防止错误的发生这种行为在g++7和clang++5中都存在。

    这几乎可以肯定是实例化问题。如果您做了一些触发pair<int, Val>的定义在main中实例化的事情,那么您就会得到错误。否则,它只在vector::emplace被实例化时被实例化,这里讨论的实现将其推迟到翻译单元的末尾(这是允许的,请参见[temp.point]/8),在这一点上,赋值运算符是完全定义和可调用的。

    CCD_ 17触发CCD_ 18定义的实例化,因为它是ADL所必需的。如果您写入::a(v)(a)(v)(均抑制ADL),则错误将消失。(显然,a.back().first需要pair<int, Val>的实例化:您正在访问其数据成员。)