c++ std::is_swappable_v在可交换类模板上为false

u3r8eeie  于 2023-04-01  发布在  其他
关注(0)|答案(1)|浏览(198)

我正在尝试编写一个可交换的类模板S<T>。如果T是可交换的,我希望S<T>S<T&>也是可交换的(例如S<int>应该可以与S<int&>交换)。所以我写了这样的东西:

#include <type_traits>

namespace n {

template <typename T>
struct S {};

// Overload #1
template <typename T, typename U>
void swap(S<T>&, S<U>&) {}

// Overload #2
// template <typename T>
// void swap(S<T>&, S<T>&) {}

}  // namespace n

int main() {
    n::S<int> sInt;
    swap(sInt, sInt);
    // static_assert(std::is_swappable_v<decltype(sInt)>);
    //               ^ assertion fails unless I uncomment #2

    n::S<int&> sIntRef;
    swap(sInt, sIntRef);
    static_assert(
        std::is_swappable_with_v<decltype(sInt)&, decltype(sIntRef)&>);
    //  ^ assertion evaluates the way I expect
}

正如你在上面看到的,is_swappable_v<n::S<int>>false,即使对swap(sInt, sInt)的调用编译了。为了让is_swappable_v<n::S<int>>计算为true,我需要取消swap的重载#2的注解。
是我做错了什么,还是类型trait std::is_swappable实际上应该这样工作?

8nuwlpux

8nuwlpux1#

表达式swap(sInt, sInt)实际上并不执行swap,因为std::swap不可见(通过using std::swap;)。
当你拥有它时:

int main() {
    n::S<int> sInt;

    using std::swap;
    swap(sInt, sInt);
}

它不会编译,因此类型实际上不能与自身交换。
原因是有两种可能的过载:

template <typename T, typename U>
void n::swap(S<T>&, S<U>&);
// with T = int, S = int

template <typename T>
void std::swap(T&, T&);
// with T = n::S<int>

第一个更好,因为每个参数都更专业。
第二个会更好,因为两个参数是相同的,它们来自同一个模板参数。
由于它们在不同方面都更好,这使得它成为一个模糊的呼叫。
添加第二个模板重载时:

template <typename T>
void n::swap(S<T>&, S<T>&);
// with T = int

这比第一个n::swap更好,因为它有重复的参数,也比std::swap更好,因为它更特殊,所以它是明确选择的。

相关问题