C++ 函数模板调用自身并陷入递归

问题描述 投票:0回答:2

这是代码:

#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。

c++ templates
2个回答
0
投票

问题是

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));
}

0
投票

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) {...}
© www.soinside.com 2019 - 2024. All rights reserved.