std::optional中将移动构造函数从重载决议移除是什么含义?
C++14 实现 std::optional 移动构造问题解答
「从重载决议中移除」的具体含义
当 std::is_move_constructible<T>::value 为 false 时,该移动构造函数完全不会被纳入重载决议的候选集合,效果和从未声明过这个函数一致。
它和标记为删除函数的核心区别是:被delete的函数仍然属于候选集合,只是被重载决议选中后会直接触发「调用已删除函数」的编译错误;而移除出候选集后,编译器会跳过该函数,继续寻找其他可行的重载(比如拷贝构造函数)。
为什么删除函数和基础SFINAE写法不生效
- 直接写
optional(optional&&) = delete不符合要求,原因如上,删除函数仍会参与重载决议,当你期望编译器自动 fallback 到拷贝构造时,会直接报调用删除函数的错误。 - 直接将SFINAE条件绑定到类模板参数
T无法生效:类实例化时T的类型已经确定,构造函数的签名如果直接使用T做判断,不属于SFINAE要求的「即时上下文」,会直接触发硬编译错误,不会触发「替换失败非错误」的规则。
C++14 下的正确实现方案
你需要给移动构造函数新增一个私有模板参数,把SFINAE的判断条件绑定到这个新增的模板参数上,构造合法的SFINAE上下文:
#include <type_traits> #include <utility> template <typename T> class optional { // 存储逻辑示例,用aligned_union存储T的实例 using storage_t = typename std::aligned_union<1, T>::type; storage_t storage; bool has_value = false; public: // 符合要求的移动构造实现 template <typename U = T, typename std::enable_if< std::is_move_constructible<U>::value, int >::type = 0> optional(optional&& other) noexcept(std::is_nothrow_move_constructible<T>::value) : has_value(other.has_value) { if (has_value) { ::new (static_cast<void*>(&storage)) T(std::move(*reinterpret_cast<T*>(&other.storage))); // 按需处理源optional的状态标记,比如将other.has_value置为false } } // 其他成员逻辑省略 };
如果需要满足「std::is_trivially_move_constructible<T>::value为true时,构造函数为constexpr且平凡」的要求,因为用户自定义的构造函数无法是平凡的,你需要补充分层偏特化逻辑:
- 定义基类模板
optional_storage<T, bool IsTriviallyMoveConstructible> - 第二个参数为
true的偏特化版本中,使用默认移动构造函数,同时配合SFINAE控制其存在条件 - 第二个参数为
false的偏特化版本中,实现自定义的移动构造逻辑 - 最终的
optional<T>继承自对应的特化基类即可
内容的提问来源于stack exchange,提问作者itzjackyscode
相关产品推荐
相关产品推荐

