如何使用C++获取指定的重载函数地址

 更新时间:2022年06月09日 10:53:14   作者:mkckr0  
重载函数是完全不同的几个函数,有不同的函数地址,下面这篇文章主要给大家介绍了关于如何使用C++获取指定的重载函数地址的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

刚刚看到一篇博客,说 std::bind 无法绑定正确的重载函数。这里的问题并不是 std::bind 能力不足,而是将函数名传递给 std::bind 时编译器无法取到这个函数的地址(也就是符号,编译器会先解析成符号,链接器再替换为地址),因为有多个重载函数都是这个名字。核心问题是无法通过函数名取到想要的重载函数地址。就像下面的代码无法编译通过:

#include <iostream>
void f()
{
    std::cout << "f 1" << std::endl;
}

void f(int x)
{
    std::cout << "f 2 " << x << std::endl;
}

int main()
{
    auto p = &f;
}

编译错误:

/home/abc/cpplearn/overload_func.cpp: In function ‘int main()’:
/home/abc/cpplearn/overload_func.cpp:15:15: error: unable to deduce ‘auto’ from ‘& f’
   15 |     auto p = &f;
      |               ^
/home/abc/cpplearn/overload_func.cpp:15:15: note:   couldn’t deduce template parameter ‘auto’

有没有什么比较完美的解决办法呢?我觉得一定有,因为 C 语言没有函数重载,函数地址作为实参也是常规操作。相比之下,C++ 引入了函数重载,却无法取到函数地址,这就很尴尬。C++ 设计者肯定也想到了这个问题。

于是查阅了 cppreference.com,看到了 Address of an overloaded function。函数名的重载解析除了发生在函数调用的时候,也会发生在以下 7 种语境:

#ContextTarget
1initializer in a declaration of an object or referencethe object or reference being initialized
2on the right-hand-side of an assignment expressionthe left-hand side of the assignment
3as a function call argumentthe function parameter
4as a user-defined operator argumentthe operator parameter
5the return statementthe return type of a function
6explicit cast or static_cast argumentthe target type of a cast
7non-type template argumentthe type of the template parameter

当函数名存在于这 7 种语境时,会发生重载解析,并且会选择与 Target 类型匹配的那个重载函数。这里就不一一考察这 7 种语境了,有兴趣可以自己查阅 cppreference.com。这里重点考察第 3 种和第 6 种。

先看第 3 种语境。当函数名作为函数调用的实参时,重载解析会选择和形参类型相匹配的版本。也就是说,下面的代码会如期运行:

#include <iostream>
void f()
{
    std::cout << "f 1" << std::endl;
}

void f(int x)
{
    std::cout << "f 2 " << x << std::endl;
}

void call(void p(int)) {
    p(1);
}

int main()
{
    call(f);
}

这段代码输出:

f 2 1

回到最初的问题,std::bind 也是函数,为什么无法正常编译呢?直接分析下面代码的编译错误信息:

#include <iostream>
#include <functional>

void f()
{
    std::cout << "f 1" << std::endl;
}

void f(int x)
{
    std::cout << "f 2 " << x << std::endl;
}

int main()
{
    auto new_func = std::bind(f, std::placeholders::_1);
    new_func(66);
}

编译错误:

/home/abc/cpplearn/overload_func.cpp: In function ‘int main()’:
/home/abc/cpplearn/overload_func.cpp:16:30: error: no matching function for call to ‘bind(<unresolved overloaded function type>, const std::_Placeholder<1>&)’
   16 |     auto new_func = std::bind(f, std::placeholders::_1);
      |                     ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~

可以看到,std::bind 准确地说是一个函数模板。它要根据其参数进行模板实参推导,再替换模板形参进行实例化(Instantiation),产生和普通函数类似的汇编代码。std::bind 进行实例化的时候,函数 f 还没有进行重载解析,其类型为<unresolved overloaded function type>。std::bind 无法进行实例化。怎样修改可以解决这个问题呢?

可以利用第 6 个语境,也就是显示转换或 static_cast。重载解析会选择与它们的目标类型相匹配的版本。下面的代码会如期运行:

#include <iostream>
#include <functional>

void f()
{
    std::cout << "f 1" << std::endl;
}

void f(int x)
{
    std::cout << "f 2 " << x << std::endl;
}

int main()
{
    auto new_func = std::bind((void(*)(int))f, std::placeholders::_1);
    new_func(66);
}

这段代码输出:

f 2 66

还有一种更加巧妙的办法,依然是利用第 3 种语境。既然 std::bind 的第一个模板实参的推导,和 f 的重载解析相矛盾。为什么不直接解决这个矛盾,将第一个模板实参改为显示指定?来看下面的代码:

#include <iostream>
#include <functional>

void f()
{
    std::cout << "f 1" << std::endl;
}

void f(int x)
{
    std::cout << "f 2 " << x << std::endl;
}

int main()
{
    auto new_func = std::bind<void(int)>(f, std::placeholders::_1);
    new_func(66);
}

这段代码如期输出:

f 2 66

总结

到此这篇关于如何使用C++获取指定的重载函数地址的文章就介绍到这了,更多相关C++获取重载函数地址内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c语言实现奇偶排序算法

    c语言实现奇偶排序算法

    这篇文章主要介绍了c语言实现奇偶排序算法,有需要的朋友可以参考一下
    2013-12-12
  • C++多线程互斥锁和条件变量的详解

    C++多线程互斥锁和条件变量的详解

    这篇文章主要为大家详细介绍了C++多线程互斥锁和条件变量,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • 详解C++ 拷贝构造函数和赋值运算符

    详解C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数、什么情况下调用赋值运算符。最后,简单的分析了下深拷贝和浅拷贝的问题。有需要的朋友可以看下
    2016-12-12
  • C语言全面梳理文件操作方法

    C语言全面梳理文件操作方法

    这篇文章主要为大家详细介绍了C语言的文件操作,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-05-05
  • C++11新特性之四种类型转换cast说明

    C++11新特性之四种类型转换cast说明

    类型转换是项目中常使用的一种语法规则,几乎每个编程语言都不可避免的涉及到这方面,下面这篇文章主要给大家介绍了关于C++11新特性之四种类型转换cast说明的相关资料,需要的朋友可以参考下
    2023-02-02
  • OpenCV图像处理之常见的图像灰度变换

    OpenCV图像处理之常见的图像灰度变换

    这篇文章主要介绍了OpenCV图像处理之常见的图像灰度变换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • 老生常谈c++中的静态成员

    老生常谈c++中的静态成员

    有时候需要类的一些成员与类本身相关联,而不是与类的每个对象相关联。比如类的所有对象都要共享的变量,这个时候我们就要用到类的静态成员,今天通过实例代码给大家详细介绍,需要的朋友参考下吧
    2021-07-07
  • C++指针和数组:字符和字符串、字符数组的关联和区别

    C++指针和数组:字符和字符串、字符数组的关联和区别

    字符串是一种重要的数据类型,但是c语言并没有显示的字符串数据类型,因为字符串以字符串常量的形式出现或者存储于字符数组中。在C++标准模板库(STL)中提供了string类,实现了对字符串的封装。
    2022-12-12
  • C++中整形与浮点型如何在内存中的存储详解

    C++中整形与浮点型如何在内存中的存储详解

    大家好!这期和大家分享整形和浮点型是如何在数据是如何在内存中存储,下面文章具有一定的参考价值,需要的小伙伴可以参考一下
    2022-05-05
  • C语言字母转换大小写的3种方法图文详解

    C语言字母转换大小写的3种方法图文详解

    我们在C语言入门的时候都会遇到要求写大小写转换的题目,所以下面这篇文章主要给大家介绍了关于C语言字母转换大小写的3种方法,文中给了详细的代码示例,需要的朋友可以参考下
    2023-10-10

最新评论