为何带有拷贝构造函数且删除移动构造的类无法自转换?
关于
std::is_convertible与删除移动构造的类的问题 先看这段代码:
#include <type_traits> struct OnlyCopyable { OnlyCopyable() { } ~OnlyCopyable() { } OnlyCopyable(const OnlyCopyable &src) { } OnlyCopyable& operator=(const OnlyCopyable &other) { return *this; } OnlyCopyable(OnlyCopyable &&other) = delete; OnlyCopyable& operator=(OnlyCopyable &&other) = delete; }; static_assert(std::is_convertible_v<OnlyCopyable, OnlyCopyable>);
这段代码中的static_assert会失败,提示OnlyCopyable无法完成自转换;但如果把两个删除移动操作符的代码注释掉,断言就能正常通过。
原因分析
根据std::is_convertible的定义,它会检查虚构函数To test() { return std::declval<From>(); }是否合法。这里From和To都是OnlyCopyable,而std::declval<OnlyCopyable>()返回的是右值引用类型。
在C++的重载决议规则中,右值会优先匹配移动构造函数——哪怕这个移动构造函数是被显式delete的。因为删除的函数仍然会参与重载决议,当它被选中后,整个表达式就会变成非法,直接导致std::is_convertible_v<OnlyCopyable, OnlyCopyable>返回false。
而如果注释掉删除移动构造的代码,编译器不会生成可用的移动构造函数(因为类已经显式定义了拷贝构造,默认移动构造会被弃置),此时重载决议中没有移动构造这个候选,就会转而匹配拷贝构造函数。这时候虚构的test()函数是合法的,断言也就通过了。
简单来说:显式删除移动构造会让它进入重载候选列表,且优先级高于拷贝构造,选中后直接导致转换失败;不手动删除的话,移动构造不会出现在候选列表里,拷贝构造就能正常被选中,转换合法。
内容的提问来源于stack exchange,提问作者Jimbo
相关产品推荐
相关产品推荐

