SFINAE无法移除方法引发‘void引用’错误,如何解决?
问题:当SpecArg为void时,如何禁止生成第二个call方法?
相关代码
#include <type_traits> template <typename SpecArg = void> struct Test { void call() {} template<typename S = SpecArg, std::enable_if_t<!std::is_void_v<S>,int> = 0> void call(SpecArg&& arg) {} };
测试场景(SpecArg为void)
int main (int argc, char **argv) { Test t; t.call(); return 0; }
报错信息
error: forming reference to void
void call(SpecArg&& arg) {}
解决方案
问题根源在于:即使enable_if的条件不满足,编译器仍会优先检查函数签名的合法性。当SpecArg是void时,SpecArg&&等价于void&&,这是C++不允许的写法,所以模板实例化阶段直接报错,根本轮不到enable_if去禁用这个函数。
要解决这个问题,只需把函数参数的类型和被enable_if约束的模板参数S绑定,而非直接使用SpecArg:
修改后的代码:
#include <type_traits> template <typename SpecArg = void> struct Test { void call() {} template<typename S = SpecArg, std::enable_if_t<!std::is_void_v<S>,int> = 0> void call(S&& arg) {} // 将SpecArg&&替换为S&& };
这样一来,当SpecArg为void时,模板参数S的默认值是void,但enable_if的条件!std::is_void_v<S>不成立,这个模板函数就不会被实例化,自然也就不会出现void&&的非法签名问题。
你也可以用另一种等价写法:
#include <type_traits> template <typename SpecArg = void> struct Test { void call() {} template<typename S = SpecArg, typename = std::enable_if_t<!std::is_void_v<S>>> void call(S&& arg) {} };
核心逻辑都是让函数参数的类型依赖于被条件约束的模板参数,确保只有条件满足时,参数类型才是合法的。
内容的提问来源于stack exchange,提问作者Felix.leg
相关产品推荐
相关产品推荐

