Here’s the code:
#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 datan");
}
template<typename __T, typename ...__Ts>
void func(const CTest<__T, __Ts...> &obj)
{
printf("obj->datan");
printf("call %lxn", func<__Ts...>);
func<__Ts...>(obj);
}
int main()
{
CTest<int> b;
printf("func<>'s addr: %lxn", func<>);
func(b);
return 0;
}
I want func<>
to be call when __Ts
is empty. However, void func(const CTest<__T, __Ts...> &obj)
recursively calls itself when __Ts
is empty. The address printed in printf("call %lxn", func<__Ts...>)
is correct, which equal to func<>
.
Changing the definition of void func(const CTest<__T, __Ts...> &obj)
as below can solve the problem. Very weird.
template<typename __T, typename ...__Ts>
void func(const CTest<__T, __Ts...> &obj)
{
printf("obj->datan");
printf("call %lxn", func<__Ts...>);
auto p = func<__Ts...>;
p(obj);
}
Why the function template calls itself while the address is correct? I am a beginner of C++, is it a C++ rule? Thank you for your help.
The enviroment is WSL, ubuntu 22.04, g++ 11.4.0, std=c++20.
2
Answers
The issue is that
func<Ts...>(obj)
can still deduce arguments. WhenTs...
is an empty pack, you are not callingfunc<>
. You need to ensure that you pass aCTest<Ts...>
, not aCTest<T, Ts...>
.func<__Ts...>(obj);
– here, despite you manually specifying the template arguments, the compiler still performs deduction and appends one extra type to the pack, based onobj
‘s type. Apparently this gets priority over convertingobj
to a different type.auto p = func<__Ts...>; p(obj);
– here you prevented template argument deduction; template arguments get baked intop
‘s type when deducing theauto
, and then aren’t touched again.One way to fix this is
func(static_cast<const CTest<__Ts...> &>(obj));
.There’s a lot of weird things in your code, starting with
This is a explicit specialization of the first
foo
. Probably not what you intended. You end up with two overloads offunc
as following:1st overload:
template<typename ...__T> void func(const CTest<__T...> &)
– declared but not defined for some reason.This one has the explicit specialization:
template<> void func(const CTest<> &obj)
2nd overload:
template<typename __T, typename ...__Ts> void func(const CTest<__T, __Ts...> &obj)
Specializing function templates is usually best avoided. Just make that overload non-template: