如何查找特定重载函数的内存地址?

2026-05-05 17:491阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1376个文字,预计阅读时间需要6分钟。

如何查找特定重载函数的内存地址?

刚刚看到一篇博客,说std::bind无法绑定正确的重载函数。这里的问题并不是std::bind能力不足,而是将函数名传递给std::bind时,编译器无法获取到这个函数的地址(也就是符号)。

刚刚看到一篇博客,说 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 种语境:

# Context Target 1 initializer in a declaration of an object or reference the object or reference being initialized 2 on the right-hand-side of an assignment expression the left-hand side of the assignment 3 as a function call argument the function parameter 4 as a user-defined operator argument the operator parameter 5 the return statement the return type of a function 6 explicit cast or static_cast argument the target type of a cast 7 non-type template argument the 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 种语境。既然隐式实例化会进行模板实参推导,和重载解析相矛盾。为什么不直接解决这个矛盾,将隐式实例化改为显示实例化?来看下面的代码:

#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

本文共计1376个文字,预计阅读时间需要6分钟。

如何查找特定重载函数的内存地址?

刚刚看到一篇博客,说std::bind无法绑定正确的重载函数。这里的问题并不是std::bind能力不足,而是将函数名传递给std::bind时,编译器无法获取到这个函数的地址(也就是符号)。

刚刚看到一篇博客,说 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 种语境:

# Context Target 1 initializer in a declaration of an object or reference the object or reference being initialized 2 on the right-hand-side of an assignment expression the left-hand side of the assignment 3 as a function call argument the function parameter 4 as a user-defined operator argument the operator parameter 5 the return statement the return type of a function 6 explicit cast or static_cast argument the target type of a cast 7 non-type template argument the 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 种语境。既然隐式实例化会进行模板实参推导,和重载解析相矛盾。为什么不直接解决这个矛盾,将隐式实例化改为显示实例化?来看下面的代码:

#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