含enable_if的可变参数模板为何编译失败?
enable_if置于模板参数的问题 背景与代码示例
最初的无约束模板函数可正常编译:
template <typename... Ts> void function(std::string name, const Ts&... files) { // 处理Ts,但不保证Ts是std::string的子类 }
为约束Ts必须是std::string的子类,实现了以下类型检查模板:
template <typename Base, typename T, typename... Ts> struct are_base_of : std::conditional<std::is_base_of<Base, T>::value, are_base_of<Base, Ts...>, std::false_type>::type {}; template <typename Base, typename T> struct are_base_of<Base, T> : std::is_base_of<Base, T> {};
可编译的版本
将std::enable_if放在返回值位置,代码可正常编译:
template <typename... Ts> typename std::enable_if<are_base_of<std::string, Ts...>::value, void>::type function(std::string name, const Ts&... files) { // 处理Ts,确保Ts是std::string的子类 }
编译失败的版本
将std::enable_if作为模板参数时,代码无法编译:
template <typename... Ts, typename std::enable_if<are_base_of<std::string, Ts...>::value>> void function(std::string name, const Ts&... files) { // 处理Ts,确保Ts是std::string的子类 }
报错信息:
error C3547: template parameter 'unnamed-parameter' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'MyClass::function'
编译失败原因
C++模板参数推导有明确规则:模板参数包Ts...是可变参数,它会匹配所有能对应推导的类型,而位于参数包之后的模板参数,无法从函数参数中获得推导依据。
此处的std::enable_if<...>作为模板参数,既没有指定默认值,也没有对应的函数参数能让编译器推导其类型,导致编译器无法确定该参数的具体类型,因此抛出错误。
修复方法
有两种可行的修复方式:
保留返回值位置的
enable_if:这是最常用的方案,利用SFINAE(替换失败不是错误)机制筛选有效重载,也就是你已经能编译的版本。给
enable_if模板参数添加默认值:如果一定要将enable_if放在模板参数中,需为其指定默认值,让编译器无需推导即可确定类型:
template <typename... Ts, typename = std::enable_if_t<are_base_of<std::string, Ts...>::value>> void function(std::string name, const Ts&... files) { // 处理Ts,确保Ts是std::string的子类 }
注:std::enable_if_t是C14引入的语法糖,等价于typename std::enable_if<...>::type,若使用C11则需写完整形式。
内容的提问来源于stack exchange,提问作者traveh

