无法通过可变参数函数将函数指针传递给父类中的方法--编译器错误?
can't pass function pointer to method in parent class through a variadic function--compiler bug?
假设你有两个结构,Generic_A
和Generic_B
。 Generic_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("%sn", __PRETTY_FUNCTION__); };
};
struct Generic_B : public Generic_A
{
void p2() { printf("%sn", __PRETTY_FUNCTION__); };
};
template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) ) {
printf("%sn", __PRETTY_FUNCTION__);
}
template <class T>
void case2( void (T::*p)() ) {
printf("%sn", __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
的成员。 表达式类型取决于找到的成员的定义,而不是限定 id 中使用的类型。
但拥有也是合法的
void (Generic_B::*member)() = &Generic_B::p1;
因为存在从void (Generic_A::*)()
到void (Generic_B::*)()
的隐式转换.
模板用作函数调用时,编译器都会经历三个基本步骤(或尝试(:
将任何显式模板参数替换为函数声明中的模板参数。
对于仍涉及至少一个模板参数的每个函数参数,将相应的函数参数与该函数参数进行比较,以(可能(推断出这些模板参数。
将推导的模板参数替换为函数声明。
在本例中,我们有函数模板声明
template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) );
其中模板参数为 T
和 ARGS
,以及函数调用表达式
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:
通常,推导过程会尝试查找模板参数值,以使推导的
A
与A
相同(在如上所述转换类型A
之后(。 但是,有三种情况允许差异:
如果原始
P
是参考类型,则推导的A
(即参考所指的类型(可能比转换后的A
更符合 cv 资格。转换后的
A
可以是另一个指针或指向成员类型的指针,可以通过限定转换 (4.4( 将其转换为推导的A
。如果
P
是一个类,并且P
具有简单模板 id 的形式,则转换后的A
可以是推导A
的派生类。 同样,如果P
是指向 simple-template-id 形式的类的指针,则转换后的A
可以是指向推导的A
指向的派生类的指针。
因此,类型推导允许某些隐式转换,涉及const
/volatile
和/或派生到基数的转换,但不考虑指向成员的指针的隐式转换。
在case1
示例中,类型推断失败,并且函数不匹配。
遗憾的是,无法显式指定模板参数包ARGS
应替换为空列表。 正如您已经发现的那样,您可以通过自己显式执行指向成员函数转换的必要指针来使其工作,即使它作为隐式转换是有效的。
- 父类的私有函数会导致对具有相同名称和相似参数的子类中的公共函数的不明确调用
- 两个父类的构造函数的序列
- C++调用使用重写函数的父类函数
- 在父类中公开受保护的构造函数
- 从父类的向量访问子类函数,而无需向下转换
- 将派生类的构造函数声明为父类的友元
- 如何从子类的构造函数初始化父类的私有成员
- 具有纯虚函数和指针数组对象类型的父类的指针数组
- 在父类函数中创建子类 - 可能吗?
- 调用父类函数来比较父类和子类
- 从多重继承中的派生类函数调用适当的父类函数
- 是否有 lint 工具可以检查子类虚拟函数是否与父类定义匹配?
- 无法使父类的虚拟函数正常工作
- 重写子类构造函数中的父类变量
- 构造函数将父类作为参数
- 父类有 26 个构造函数重载.如何在不复制+粘贴 26 个重载的情况下将一个小任务附加到所有构造器?
- 如果不手动完成,子类是否继承父类的析构函数?
- 当函数返回类型为父类时,如何返回子类的对象?
- 将子类的成员函数强制转换为父类
- 如何将 std::unique_ptr<Parent> 与具有受保护虚拟析构函数的只读父类一起使用