C/C++中获取重载函数地址的方法

 更新时间:2024年04月02日 09:29:14   作者:流星雨爱编程  
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这 些同名函数的形参列表不同,常用来处理实现功能类似数据类型不同的问题,本文给大家介绍了C/C++中获取重载函数地址的方法,需要的朋友可以参考下

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()
{
    //方法2
	CFunc12345 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 syntax
     connect(slider, SIGNAL(valueChanged(int)),
             lcd, SLOT(display(int)));
 
     // Functor-based syntax, first alternative
     connect(slider, &QSlider::valueChanged,
             lcd, static_cast<void (QLCDNumber::*)(int)>(&QLCDNumber::display));
 
     // Functor-based syntax, second alternative
     void (QLCDNumber::*mySlot)(int) = &QLCDNumber::display;
     connect(slider, &QSlider::valueChanged,
             lcd, mySlot);
 
     // Functor-based syntax, third alternative
     connect(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中使用信号和槽机制时,处理函数重载的情况变得更加简单和直观。

以上就是C/C++中重载函数取地址的方法的详细内容,更多关于C/C++重载函数取地址的资料请关注脚本之家其它相关文章!

相关文章

  • C语言 动态内存管理全面解析

    C语言 动态内存管理全面解析

    动态内存是相对静态内存而言的。所谓动态和静态就是指内存的分配方式。动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存,本文带你深入探究C语言中动态内存的管理
    2022-02-02
  • C语言返回值指针的函数详解

    C语言返回值指针的函数详解

    这篇文章主要为大家详细介绍了C语言返回值指针的函数,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • c++实现扫雷小游戏代码分享

    c++实现扫雷小游戏代码分享

    这篇文章主要介绍了c++实现扫雷小游戏并附上代码分享,代码不能够实现当所查坐标周围雷的数量为0时,直接展开周围坐标,但有一点的知识性参考价值,需要的小伙伴可以参考一下
    2022-02-02
  • C语言模拟掷骰子游戏

    C语言模拟掷骰子游戏

    这篇文章介绍了C语言模拟掷骰子游戏的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。需要的朋友可以收藏下
    2021-11-11
  • 最短时间学会基于C++实现DFS深度优先搜索

    最短时间学会基于C++实现DFS深度优先搜索

    常见使用深度优先搜索(DFS)以及广度优先搜索(BFS)这两种搜索,今天我们就来讲讲什么是深度优先搜索,感兴趣的可以了解一下
    2021-08-08
  • C++中getline()和get()的方法浅析

    C++中getline()和get()的方法浅析

    大家都知道作为C++获取输入流的方法,几乎在任何一本资料书上getline()方法和get()方法都作为入门级的方法进行讲述,即便如此,笔者在学习C++的过程中仍经常忘记这二者的使用要点,可能也有C++的初学者对这两个方法还心存疑虑,本篇文章就这两个方法的使用进行简要阐述。
    2016-10-10
  • C++中std::find函数介绍和使用场景

    C++中std::find函数介绍和使用场景

    std::find函数是一个非常实用的通用查找算法,适用于各种场景,本文主要介绍了C++中std::find函数介绍和使用场景,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • C语言memset函数使用方法详解

    C语言memset函数使用方法详解

    这篇文章主要介绍了C语言memset函数使用方法详解的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下
    2017-10-10
  • C语言可变参数与内存管理超详细讲解

    C语言可变参数与内存管理超详细讲解

    有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数。C 语言为这种情况提供了一个解决方案,这篇文章主要介绍了C语言可变参数与内存管理
    2023-01-01
  • C语言实现扫雷游戏详细代码实例

    C语言实现扫雷游戏详细代码实例

    这篇文章主要介绍了C语言实现扫雷游戏详细代码实例,有感兴趣的同学可以借鉴参考下
    2021-02-02

最新评论