可变参数模板重载解析:为何基类参数无法匹配派生类型?
为什么基类参数的可变参数模板重载无法匹配派生类?
我想为某一类型及其所有派生类创建带可变参数模板的函数重载。一开始只写了以基类作为首个参数的重载,但这个重载匹配不了派生类型。加上带concept或enable_if的第三个重载后就能匹配了(这时第一个重载其实没必要),但我搞不懂为什么调用派生类型时,基类参数的重载没被重载决议考虑。
代码示例:
#include <iostream> struct A {}; struct B : public A {}; template<class... Types> void func(Types... args) { std::cout << "1"; } template<class... Types> void func(const A& a, Types... args) { std::cout << "2"; } // 为何需要这个重载才能覆盖派生类? // 启用该函数时输出"3",禁用时输出"1" #if 1 template<typename dA, class... Types> requires std::derived_from<dA, A> void func(const dA& a, Types... args) { std::cout << "3"; } #endif int main() { func(B{}); }
进一步明确问题:为什么禁用第三个重载时输出1,启用时输出3?
核心原因:模板重载决议的匹配优先级与特化规则
1. 基类参数重载未被选中的原因
调用func(B{})时,编译器会筛选可行的模板重载:
- 第一个重载是接受任意参数的可变参数模板,推导后直接匹配
func(B),完全不需要任何隐式转换。 - 第二个重载要求第一个参数是
const A&,要匹配的话必须把B隐式转换为const A&。虽然这是一个可行函数,但在重载决议的优先级排序中,完全匹配的模板实例化比需要隐式转换的实例化优先级更高,所以编译器会优先选第一个重载,输出1。
2. 带concept的重载被选中的原因
第三个重载通过std::derived_from限定了第一个参数必须是A的派生类(包括A本身),推导后得到func(const B&),同样是完全匹配实参,不需要转换。此时和第一个重载比,根据C++模板的部分排序规则:更特化的模板会被优先选中。第三个重载只针对A的派生类,而第一个重载接受任意类型,显然前者更特化,因此编译器会选它,输出3。
简化实现
其实不需要前两个重载,只用带concept的版本就能覆盖基类和所有派生类(因为std::derived_from<A,A>在C++20中为true):
#include <iostream> #include <concepts> struct A {}; struct B : public A {}; template<typename dA, class... Types> requires std::derived_from<dA, A> void func(const dA& a, Types... args) { std::cout << "3"; } int main() { func(A{}); // 匹配成功,输出3 func(B{}); // 匹配成功,输出3 }
内容的提问来源于stack exchange,提问作者ridilculous
相关产品推荐
相关产品推荐

