这是代码:
#include <stdio.h>
template<typename ...__T>
struct CTest {
};
template<typename __T, typename ...__Ts>
struct CTest<__T, __Ts...>: public CTest<__Ts...> {
};
template<typename ...__T>
void func(const CTest<__T...> &);
template<typename __T, typename ...__Ts>
void func(const CTest<__T, __Ts...> &);
template<>
void func(const CTest<> &obj)
{
printf("no data\n");
}
template<typename __T, typename ...__Ts>
void func(const CTest<__T, __Ts...> &obj)
{
printf("obj->data\n");
printf("call %lx\n", func<__Ts...>);
func<__Ts...>(obj);
}
int main()
{
CTest<int> b;
printf("func<>'s addr: %lx\n", func<>);
func(b);
return 0;
}
我希望当
func<>
为空时调用 __Ts
。然而,当 void func(const CTest<__T, __Ts...> &obj)
为空时,__Ts
会递归调用自身。 printf("call %lx\n", func<__Ts...>)
中打印的地址是正确的,等于func<>
。
改变
void func(const CTest<__T, __Ts...> &obj)
的定义如下可以解决问题。很奇怪。
template<typename __T, typename ...__Ts>
void func(const CTest<__T, __Ts...> &obj)
{
printf("obj->data\n");
printf("call %lx\n", func<__Ts...>);
auto p = func<__Ts...>;
p(obj);
}
为什么地址正确的时候函数模板却调用了自己?我是C++初学者,这是C++规则吗?谢谢您的帮助。
环境是WSL,ubuntu 22.04,g++ 11.4.0,std=c++20。
问题是
func<Ts...>(obj)
仍然可以推导出参数。当 Ts...
是空包时,您 不会 呼叫 func<>
。您需要确保您通过了 CTest<Ts...>
,而不是 CTest<T, Ts...>
。
template<typename T, typename ...Ts>
void func(const CTest<T, Ts...> &obj)
{
printf("obj->data\n");
printf("call %p\n", static_cast<void *>(&func<Ts...>));
func(static_cast<const CTest<Ts...> &>(obj));
}
func<__Ts...>(obj);
- 在这里,尽管您手动指定模板参数,编译器仍然会根据 obj
的类型执行推导并向包附加一种额外类型。显然,这比将 obj
转换为其他类型优先。
auto p = func<__Ts...>; p(obj);
- 在这里你阻止了模板参数推导;在推导 p
时,模板参数会被烘焙到 auto
的类型中,然后不再被触及。
解决此问题的一种方法是
func(static_cast<const CTest<__Ts...> &>(obj));
。
你的代码中有很多奇怪的东西,从
开始template<>
void func(const CTest<> &obj)
这是第一个
foo
的显式专业化。可能不是你想要的。您最终会得到两个 func
重载,如下所示:
template<typename ...__T> void func(const CTest<__T...> &)
- 声明但未定义。
它有明确的专业化:
template<> void func(const CTest<> &obj)
template<typename __T, typename ...__Ts> void func(const CTest<__T, __Ts...> &obj)
通常最好避免专门化函数模板。只需将第二个重载设置为非模板即可:
void func(const CTest<> &obj) {}
template<typename __T, typename ...__Ts>
void func(const CTest<__T, __Ts...> &obj) {...}