skip to Main Content

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


  1. The issue is that func<Ts...>(obj) can still deduce arguments. When Ts... is an empty pack, you are not calling func<>. You need to ensure that you pass a CTest<Ts...>, not a CTest<T, Ts...>.

    template<typename T, typename ...Ts>
    void func(const CTest<T, Ts...> &obj)
    {
        printf("obj->datan");
        printf("call %pn", static_cast<void *>(&func<Ts...>));
        func(static_cast<const CTest<Ts...> &>(obj));
    }
    
    Login or Signup to reply.
  2. 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 on obj‘s type. Apparently this gets priority over converting obj to a different type.

    auto p = func<__Ts...>; p(obj); – here you prevented template argument deduction; template arguments get baked into p‘s type when deducing the auto, 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

    template<>
    void func(const CTest<> &obj)
    

    This is a explicit specialization of the first foo. Probably not what you intended. You end up with two overloads of func 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:

    void func(const CTest<> &obj) {}
    
    template<typename __T, typename ...__Ts>
    void func(const CTest<__T, __Ts...> &obj) {...}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search