异常说明是否属于SFINAE的直接上下文?(可变参数构造器后续问题)
可变参数构造器的SFINAE:异常说明的陷阱
嘿,咱们接着聊之前那个可变参数构造器中的SFINAE问题!之前我给出了这么一段构造器代码,本来以为能实现预期效果:
template<typename... Args> StrongAlias(Args&&... args) noexcept(noexcept(T(std::declval<Args>()...))) : value(std::forward<Args>(args)...) {}
当时我的想法是:如果T没法从传入的实参类型构造,这个StrongAlias的构造器会自动被SFINAE禁用——毕竟异常说明里用到了T(std::declval<Args>()...),感觉能触发模板重载的筛选逻辑对吧?
但有朋友一针见血地指出这行不通,核心原因就是:异常说明并不属于模板参数推导的直接上下文。
这里得掰扯清楚SFINAE的生效条件:只有当错误发生在模板参数推导的「直接上下文」中时,编译器才会跳过当前重载,而不是直接报错。所谓直接上下文,指的是和模板参数直接绑定、在推导阶段就能立刻判定合法性的代码部分。而异常说明里的表达式,是要等到模板实例化阶段才会被求值的——这时候如果T(std::declval<Args>()...)不合法,编译器不会触发SFINAE,反而会直接抛出编译错误。
那要实现咱们想要的「不合法构造时禁用重载」的效果,得把构造条件放到直接上下文里才行。比如用C++20的requires表达式,写法简洁直观:
template<typename... Args> requires std::constructible_from<T, Args...> StrongAlias(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>) : value(std::forward<Args>(args)...) {}
如果是C++20之前的版本,就用std::enable_if来做:
template<typename... Args, std::enable_if_t<std::is_constructible_v<T, Args...>, int> = 0> StrongAlias(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>) : value(std::forward<Args>(args)...) {}
这样就能确保当T无法从Args...构造时,这个重载会被SFINAE正确过滤掉啦。
内容的提问来源于stack exchange,提问作者Brian Bi
相关产品推荐
相关产品推荐

