`pair::operator=(pair&&)` 错误与`auto&` 推导出移动操作-libstdc++ 回归?

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

给出这个程序:

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{});
}

...编译成功:

on 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);
}

...编译成功:

...但会在以下位置产生编译时错误:

on wandbox


产生的错误似乎与受约束的 pair operator=(pair&&) 过载有关 - from include/bits/stl_pair.h on GitHub's libstdc++ mirror:

  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;
  }

这是怎么回事?这是最新版本的 libstdc++ 中的实现缺陷吗?还是旧版本错误地编译了格式错误的代码?


编辑:正如 AndyG 在他的 (现已删除) 答案中发现的那样,在调用 [=26 之前调用空函数时也会发生错误=]:

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 中都存在。

on wandbox


Sergey Murzin, and can be tested out on 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 中都存在。

这几乎可以肯定是一个 point-of-instantiation 问题。如果您执行的操作会触发 pair<int, Val>main 中的定义的实例化,则会出现错误。否则,它仅在实例化 vector::emplace 时实例化,此处讨论的实现推迟到翻译单元的末尾(这是允许的,请参阅 [temp.point]/8),此时赋值运算符已完全定义并且可以调用。

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