目录
1.现象
2.指定参数取函数地址
3.利用Qt的类QOverload
1.现象
函数重载在C/C++编码中是非常常见的,但是我们在std::bind或std::function绑定函数地址的时候,直接取地址,程序编译就会报错,示例如下:
class CFunc12345
{
public:void func12345(int x) {std::cout << x;}void func12345(double x) {std::cout << x;}
};void func67890(int x) {std::cout << x;
}
void func67890(double x) {std::cout << x;
}int main()
{CFunc12345 x1;auto p = std::bind(&CFunc12345::func12345, x1, std::placeholders::_1); //[1]编译报错p(14124);auto p1 = std::bind(&func67890, std::placeholders::_1); //[2]编译报错p1(33333333);std::function<void(int)> p2(&func67890); //[3]编译报错p2(33333333);return 0;
}
上述代码[1],[2],[3]处都会出现编译错误,那是因为函数重载,多个函数名相同,找不到该用那个函数地址。这个时候解决办法就是人为指定用那个函数,那么人为指定用那个函数有哪些办法呢?
2.指定参数取函数地址
方法如下:
//方法1:CFunc12345 x1;using FUNC1 = void (CFunc12345::*)(int);FUNC1 func1 = &CFunc12345::func12345;auto p1 = std::bind(func1, x1, std::placeholders::_1);p1(14124);using FUNC2 = void(*)(int);FUNC2 func2 = &func67890;auto p2 = std::bind(func2, std::placeholders::_1);p2(14124);FUNC2 func3 = &func67890;std::function<void(int)> p3(func3);p3(14124);
从代码可以看出,就是手动指定函数类型,先取到函数地址后,再把地址传入指定的对象(如std::bind、std::function等)中,避免了std::bind或std::function去判断函数类型,从而避免了编译错误。
3.利用Qt的类QOverload
QOverload是Qt5中提供的一种用于重载信号和槽函数连接的方式。它允许开发者在使用信号与槽机制时,更灵活地处理函数重载的情况。通过将信号和槽函数的参数类型转换为指定类型,QOverload实现了对信号和槽函数的类型安全检查。
在实际应用中,QOverload通过重载调用QOverload::of,利用它来指定多个信号版本中的具体哪种类型参数。例如,当有一个信号函数被重载,具有多个不同参数类型的版本时,可以使用QOverload来明确指定要连接的是哪个版本的信号函数。
QOverload的作用就是指定重载函数中的函数类型,从它的源码中可以看出来,源码如下:
template <typename... Args>
struct QNonConstOverload
{template <typename R, typename T>Q_DECL_CONSTEXPR auto operator()(R (T::*ptr)(Args...)) const Q_DECL_NOTHROW -> decltype(ptr){ return ptr; }template <typename R, typename T>static Q_DECL_CONSTEXPR auto of(R (T::*ptr)(Args...)) Q_DECL_NOTHROW -> decltype(ptr){ return ptr; }
};template <typename... Args>
struct QConstOverload
{template <typename R, typename T>Q_DECL_CONSTEXPR auto operator()(R (T::*ptr)(Args...) const) const Q_DECL_NOTHROW -> decltype(ptr){ return ptr; }template <typename R, typename T>static Q_DECL_CONSTEXPR auto of(R (T::*ptr)(Args...) const) Q_DECL_NOTHROW -> decltype(ptr){ return ptr; }
};template <typename... Args>
struct QOverload : QConstOverload<Args...>, QNonConstOverload<Args...>
{using QConstOverload<Args...>::of;using QConstOverload<Args...>::operator();using QNonConstOverload<Args...>::of;using QNonConstOverload<Args...>::operator();template <typename R>Q_DECL_CONSTEXPR auto operator()(R (*ptr)(Args...)) const Q_DECL_NOTHROW -> decltype(ptr){ return ptr; }template <typename R>static Q_DECL_CONSTEXPR auto of(R (*ptr)(Args...)) Q_DECL_NOTHROW -> decltype(ptr){ return ptr; }
};
顾名思义,模版类QNonConstOverload是取不带const的类成员函数的地址的,模版类QConstOverload是取带const的类成员函数的地址的,QOverload是取普通函数地址的,它们之间的关系如下图所示:
于是上面编译错误的代码可以修改为:
int main()
{//方法2CFunc12345 x1;auto p = std::bind(QOverload<int>::of(&CFunc12345::func12345), x1, std::placeholders::_1);p(14124);auto p1 = std::bind(QOverload<int>::of(&func67890), std::placeholders::_1);p1(33333333);std::function<void(int)> p2(QOverload<int>::of(&func67890));p2(33333333);return 0;
}
QOverload看似只有装有Qt环境才能用,其实把源码稍加修改就可以作为标准的C++类来用,修改后的COverload源码如下所示:
template <typename... Args>
struct CNonConstOverload
{template <typename R, typename T>auto operator()(R(T::* ptr)(Args...)) const -> decltype(ptr){return ptr;}template <typename R, typename T>static auto of(R(T::* ptr)(Args...)) -> decltype(ptr){return ptr;}
};template <typename... Args>
struct CConstOverload
{template <typename R, typename T>auto operator()(R(T::* ptr)(Args...) const) const -> decltype(ptr){return ptr;}template <typename R, typename T>static auto of(R(T::* ptr)(Args...) const) -> decltype(ptr){return ptr;}
};template <typename... Args>
struct COverload : CConstOverload<Args...>, CNonConstOverload<Args...>
{using CConstOverload<Args...>::of;using CConstOverload<Args...>::operator();using CNonConstOverload<Args...>::of;using CNonConstOverload<Args...>::operator();template <typename R>auto operator()(R(*ptr)(Args...)) const -> decltype(ptr){return ptr;}template <typename R>static auto of(R(*ptr)(Args...)) -> decltype(ptr){return ptr;}
};
QOverload在Qt的信号槽中也用的比较多。信号槽连接时,使用基于字符串的语法,可以显式指定参数类型。因此,使用重载信号或槽的哪一个实例是明确的。相反,使用基于模版函数的语法,必须强制转换重载信号或槽,以告诉编译器要使用哪个实例。
例如,QLCDNumber有三个版本的display() 槽函数:
1.QLCDNumber::display(int)
2.QLCDNumber::display(double)
3.QLCDNumber::display(QString)
使用信号QSlider::valueChanged()和QLCDNumber::display(int)连接,可以有如下系列方法:
auto slider = new QSlider(this);auto lcd = new QLCDNumber(this);// String-based syntaxconnect(slider, SIGNAL(valueChanged(int)),lcd, SLOT(display(int)));// Functor-based syntax, first alternativeconnect(slider, &QSlider::valueChanged,lcd, static_cast<void (QLCDNumber::*)(int)>(&QLCDNumber::display));// Functor-based syntax, second alternativevoid (QLCDNumber::*mySlot)(int) = &QLCDNumber::display;connect(slider, &QSlider::valueChanged,lcd, mySlot);// Functor-based syntax, third alternativeconnect(slider, &QSlider::valueChanged,lcd, QOverload<int>::of(&QLCDNumber::display));// Functor-based syntax, fourth alternative (requires C++14)connect(slider, &QSlider::valueChanged,lcd, qOverload<int>(&QLCDNumber::display));
总的来说,QOverload是一个强大的工具,它使得在Qt中使用信号和槽机制时,处理函数重载的情况变得更加简单和直观。