在继承类中回调参数类型不匹配
Callback argument type does not match in inherited class
我试图实现基于链接代码的事件管理器在这里的顶部答案:游戏对象之间的对话
然而,当我试图注册回调时,我得到了一个错误。我确信它与类型定义有关,我承认我不确定它是如何工作的,但它在链接代码中是完全相同的形式。B类应该从接口继承,那么为什么类型不同呢?我把代码浓缩成下面这个最小的例子。
#include <iostream>
class Interface;
typedef void (Interface::*Callback)(void *data);
class Interface
{
public:
void Register (Callback func);
};
void Interface::Register(Callback func)
{
std::cout << "Register" << std::endl;
}
class B : public Interface
{
public:
B();
void Echo(void *data);
};
B::B()
{
Register( (Callback)Echo );
}
void B::Echo(void *data)
{
std::cout << "Echo" << std::endl;
}
int main()
{
B b;
return 0;
}
下面是我在g++ 4.6.1下得到的错误:
test.cpp: In constructor ‘B::B()’:
test.cpp:31:22: error: argument of type ‘void (B::)(void*)’ does not match ‘Callback {aka void (Interface::*)(void*)}’
谁能解释一下我做错了什么?由于 正如@Kerrek正确指出的那样,Echo
不是Interface
的成员,因此B::Echo
不符合Interface::*Callback
的条件。但是你可以使用模板来完成,例如:
template <class T> class Interface {
public:
typedef void (T::*Callback)(void *data);
void Register(Callback func) {
std::cout << "Register" << std::endl;
}
// ...
};
class B : public Interface<B> {
public:
B() {
Register(&B::Echo);
}
void Echo(void *data) {
// Do something
}
};
我认为你最好使用std::function (c++11)或boost::function (c++03+boost)
#include <iostream>
class Interface;
typedef void (Interface::*Callback)(void *data);
class Interface
{
public:
std::function<void(void*)> register;
Interface(std::function<void(void*)> register_)
: register(register_) //intializer list
{}
virtual ~Interface(){} //put me in
};
void Interface::Register(Callback func)
{
std::cout << "Register" << std::endl;
}
class B : public Interface
{
public:
B();
void Echo(void *data);
};
B::B()
: Interface( std::bind(B::Echo, this) )
{}
void B::Echo(void *data)
{
std::cout << "Echo" << std::endl;
}
尽管我不明白为什么你不使用纯虚拟
class Interface
{
public:
virtual void Echo(void*)=0;
};
void B::Echo(void *data) //implements Echo
{
std::cout << "Echo" << std::endl;
}
call interface->echo将调用子
如果需要性能,则使用
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern并且要非常小心void*,它们通常被认为是不好的。
在注释中编辑地址点:非纯虚拟
class Interface
{
public:
virtual ~Interface(){} //put me in
virtual void echo(void*){} //if implementation is not extended it will do nothing.
//others
};
这不是Java,接口不是语言定义的东西。这样你就可以有一个接口,你可以选择实现哪一部分,如果一个回调与你的类无关,那么就不要实现它。
void*有很多不好的原因。from c++ FAQ
避免void*(将它们保存在底层函数和数据结构中)如果您确实需要它们并提供类型安全接口,通常模板,给你的用户)
http://www2.research.att.com/bs/bs_faq.html
搜索"void*"
但是基本上void*绕过了c++中所有的类型安全。这是C中的一个hack,以弥补它没有任何多态性或泛型代码的事实。
相关文章:
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型