gpt4 book ai didi

c++ - 无法通过可变参数函数将函数指针传递给父类中的方法——编译器错误?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:26:14 29 4
gpt4 key购买 nike

假设您有两个结构,Generic_AGeneric_BGeneric_B 派生自 Generic_A。为什么当 Generic_B 尝试访问其父级 Generic_A 中的方法时,它会生成以下错误:

test2.cpp: In function 'int main()':
test2.cpp:26: error: no matching function for call to 'case1(void (Generic_A::*)()'

此代码使用 gcc 4.4.6 版编译,重现了问题:

#include <stdio.h>

struct Generic_A
{
void p1() { printf("%s\n", __PRETTY_FUNCTION__); };
};

struct Generic_B : public Generic_A
{
void p2() { printf("%s\n", __PRETTY_FUNCTION__); };
};

template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) ) {
printf("%s\n", __PRETTY_FUNCTION__);
}

template <class T>
void case2( void (T::*p)() ) {
printf("%s\n", __PRETTY_FUNCTION__);
}

main()
{
//generates error
case1<Generic_B>(&Generic_B::p1);

//compiles fine
case2<Generic_B>(&Generic_B::p1);
}

这两个函数调用之间唯一明显的区别是 case1() 有一个模板参数,而 case2() 没有。它们不应该都允许您将函数指针传递给 Generic_B 的父级(即 &Generic_B::p1)中的方法吗?

此外,在 case1 中转换函数指针似乎有时可以解决错误:

case1<Generic_B>( (void (Generic_B::*)()) &Generic_B::p1);

这是怎么回事?

最佳答案

这很棘手,但事实证明 g++ 是正确的。

首先,表达式&Generic_B::p1的类型是void (Generic_A::*)()。编译器使用 Generic_B:: 来限定其名称查找并找到 Generic_A 的成员。表达式类型取决于找到的成员的定义,而不是 qualified-id 中使用的类型。

但是拥有也是合法的

void (Generic_B::*member)() = &Generic_B::p1;

因为存在从 void (Generic_A::*)()void (Generic_B::*)() 的隐式转换。

每当将函数模板用作函数调用时,编译器都会经历三个基本步骤(或尝试):

  1. 用函数声明中的模板参数替换任何显式模板参数。

  2. 对于仍涉及至少一个模板参数的每个函数参数,将相应的函数参数与该函数参数进行比较,以(可能)推导出那些模板参数。

  3. 将推导的模板参数代入函数声明。

在这种情况下,我们有函数模板声明

template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) );

其中模板参数为TARGS,函数调用表达式

case1<Generic_B>(&Generic_B::p1)

其中显式模板参数是 Generic_B,函数参数是 &Generic_B::p1

所以第 1 步,替换显式模板参数:

void case1( void (Generic_B::*p)(ARGS...) );

第二步,比较参数类型和实参类型:

参数类型(标准部分 14.8.2 中的 P)是 void (Generic_B::*)(ARGS...)。参数类型 (A) 是 void (Generic_A::*)()

C++ 标准 (N3485) 14.8.2.1p4:

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:

  • If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the transformed A.

  • The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification conversion (4.4).

  • If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A. Likewise, if P is a pointer to a class of the form simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.

因此类型推导允许某些涉及const/volatile 和/或派生到基转换的隐式转换,但不考虑指向成员的指针的隐式转换。

case1例子中,类型推导失败,函数不匹配。

遗憾的是,无法明确指定您的模板参数包 ARGS 应替换为空列表。正如您已经发现的那样,您可以通过自己显式地执行指向成员函数转换的必要指针来实现这一点,即使它在其他方面作为隐式转换是有效的。

关于c++ - 无法通过可变参数函数将函数指针传递给父类中的方法——编译器错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18989869/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com