为什么 std::swap<std::array<int,3>> 无法编译?

shu*_*alo 21 c++ swap std

我想交换两个固定大小的整数数组。

与直觉相反,以下内容无法编译,因为没有swap找到匹配的实例。

#include <array>
#include <utility>

int main()
{
    std::array<int,3> a, b;
    std::swap< std::array<int,3> >( a, b ); 
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我觉得这很令人惊讶。但是,用编译替换交换std::swap(a,b)并且(根据 VSCode)具有签名

inline void std::swap<int, 3UL>(std::array<int, 3UL> &__one, std::array<int, 3UL> &__two)
Run Code Online (Sandbox Code Playgroud)

我也无法理解。

问:这是怎么回事?

for*_*818 17

您正在寻找的重载是(来自cppreference):

template< class T, std::size_t N >    
constexpr void swap( std::array<T, N>& lhs,
                     std::array<T, N>& rhs )
                         noexcept(/* see below */);
Run Code Online (Sandbox Code Playgroud)

正如错误报告所示,编译器无法找到std::swap()匹配的可行重载std::swap<std::array<int,3>>。

这将是显式提供模板参数的“正确”方法:

#include <array>
#include <utility>

int main()
{
    std::array<int,3> a, b;
    std::swap<int,3>( a, b ); 
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我怀疑在某种情况下你真的想这样做。

PS:您还可以在从 VSCOde 获得的签名中看到:std::swap<int, 3UL>不是std::swap<std::array<int,3UL>>。然而,查看实现有时可能会产生误导,我建议首先查阅文档。

  • 您可能想解释为什么当“T”是“std::array”时“std::swap(T&amp;, T&amp;)”不是一个很好的匹配。 (9认同)
  • @RemyLebeau,实际上我现在不再明白为什么 OP 代码没有自己编译:D 多么尴尬 (2认同)

use*_*670 10

该问题似乎是由 libstdc++ 实现引起的。它强制要求 (源tuple-like)的适当“基本”变体中使用的类型:std::swap

  template<typename _Tp>
    _GLIBCXX20_CONSTEXPR
    inline
#if __cplusplus >= 201103L
    typename enable_if<__and_<__not_<__is_tuple_like<_Tp>>,
                  is_move_constructible<_Tp>,
                  is_move_assignable<_Tp>>::value>::type
#else
    void
#endif
    swap(_Tp& __a, _Tp& __b)
Run Code Online (Sandbox Code Playgroud)

即使标准没有提到这样的限制:

20.2.2
swap [utility.swap]
template<class T>
constexpr void swap(T& a, T& b) noexcept(see below );
1 备注:该函数是指定的定制点(16.5.4.2.1),并且不应参与重载决策,除非is_move_constructible_v<T>istrue和is_move_assignable_v<T>is true。里面的表达式noexcept相当于: is_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<T>
2 要求:类型T应为Cpp17MoveConstructible(表 26)和Cpp17MoveAssignable(表 28)。

代码适用于 vc++ ,其中库仅根据标准要求检查is_move_constructible和。is_move_assignable

  • 是的,他们不应该这样做。我想您引用的措辞可以理解为说这个重载应该可以在可移动构造和移动可分配的*任何*类型上调用。然而,我认为意图很明显“……除非存在更专门的重载”,所以我认为 OP 观察到的行为是符合标准的。 (3认同)
  • @KonradRudolph 抱歉,但我根本看不到像“……除非存在更专门的重载”这样的意图。`array` 满足所有列出的可移动构造和可移动分配的要求,因此即使存在特定于数组的变体,基本重载也没有理由不接受它。这种基本重载仅对于类似元组的类型会被破坏,并且对于其他合适的类型(例如“std::string”)也可以正常工作,即使存在特定于字符串的变体。– (2认同)