如何在 C++ 中传递函数指针并在函数中进行比较?

Posted

技术标签:

【中文标题】如何在 C++ 中传递函数指针并在函数中进行比较?【英文标题】:How can you pass a function pointer and compare it in a function in C++? 【发布时间】:2018-03-29 22:06:02 【问题描述】:

出于测试原因,我有一定数量的方法,它们在 C++ 中的签名如下所示:

dvec3 (*)(dvec3, dvec3, double)

我把它们放在一个向量中,如下所示:

vector<dvec3 (*)(dvec3, dvec3, double)> methods = lerp, plerp, splerp, ilerp;

这个想法是做一个函数,它接受函数指针并返回一个字符串来标识当前正在使用的函数(即我想打印出上面 4 个函数中正在使用的函数)

为此,我尝试将方法编写如下(我故意省略了大多数情况):

string inline erpToString(dvec3 (*f)(dvec3, dvec3, double))

    if (f==lerp)
    
        return "Lerp";
    

但是上面的内容不能编译,错误消息指出这是一个转换错误。我做错了什么?

编辑:

编译器消息:

/home/kronos/Desktop/OpenGL-Template/source/Rendering/rendering.cpp: In function ‘std::__cxx11::string erpToString(glm::dvec3* (*)(glm::dvec3, glm::dvec3, double))’:
/home/kronos/Desktop/OpenGL-Template/source/Rendering/rendering.cpp:1362:7: error: invalid operands of types ‘glm::dvec3* (*)(glm::dvec3, glm::dvec3, double) aka glm::tvec3<double, (glm::precision)0>* (*)(glm::tvec3<double, (glm::precision)0>, glm::tvec3<double, (glm::precision)0>, double)’ and ‘<unresolved overloaded function type>’ to binary ‘operator==’
  if (f==lerp)
      ~^~~~~~
make[2]: *** [CMakeFiles/voxel-world.dir/build.make:111: CMakeFiles/voxel-world.dir/source/Rendering/rendering.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:68: CMakeFiles/voxel-world.dir/all] Error 2

编辑:

我已经制作了具有相同逻辑的最小可能文件,但是它不会重现错误:

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <string>

using namespace std;
using namespace glm;

dvec3 lerp(dvec3 p1, dvec3 p2, double t)

    return (1-t)*p1+t*p2;


string inline erpToString(dvec3 (*f)(dvec3, dvec3, double))

    if (f==lerp)
    
        return "Lerp";
    


int main()



使用代码显示其他方法(ilerp 和 company)确实有效。所以似乎我有一个命名空间冲突(使用命名空间是的)。 SInce GLM 定义了一个 lerp 函数,但我需要使用的是我定义的那个。有人有建议吗?

【问题讨论】:

或者,您可以将日志记录添加到这些方法中的每一个;这样,它们将帮助您跟踪调用了哪些方法以及何时调用,而无需每次都调用您的自定义 erpToString() 函数。 似乎应该可以。你能发一个minimal reproducible example吗? 我只需要将被调用的方法打印到 csv 文件中。我正在评估每种方法的某些属性,我想要的只是将每种方法作为 csv 文件部分的标题,以便能够识别数据集 好吧,编译器说你正在尝试使用返回指针glm::dvec3* (*)(glm::dvec3, glm::dvec3, double)的函数 发布的代码与错误消息不匹配。我们现在可以有minimal reproducible example 吗? 【参考方案1】:

您可以使用usingtypedef 来简化所有事情。代码对我有用

int a(float x, float y, double z)

    return int(x + y + z);


int b(float x, float y, double z)

    return int(x - y - z);



using fdef = int(*)(float, float, double); // or typedef int (*fdef)(float, float, double);

std::vector<fdef> va, b;

if (v[0] == a)
    std::cout << "qqq\n";

【讨论】:

"使用 using 来简化所有事情" - 或在 C++11 之前的编译器中使用 typedeftypdef int (*fdef)(float, float, double); @RemyLebeau 当然,但我鼓励在我看到需要时使用using 我同意,我只是说并不是每个人都在使用 C++11 或更高版本的编译器,并且 OP 的问题没有提到实际使用的是哪个版本。因此,仅提及两种可能性,以防万一,仅此而已 @RemyLebeau 很公平,编辑答案,谢谢 这是一个命名空间冲突【参考方案2】:

密钥是‘&lt;unresolved overloaded function type&gt;’

特别是,您有两个lerp 函数。这通常是允许的。在大多数情况下,C++ 会找出你想要的那个。例如,当您调用lerp(arg1,arg2, arg3) 时,C++ 将使用这三个参数进行重载解析。但是在f==lerp 中,没有用于重载解析的三个参数。这就是 C++ 说“未解决的重载”的原因。

解决方案是将lerp 转换为f 的类型:if (f==static_cast&lt;decltype(f)&gt;(&amp;lerp))。用于重载解析的三个参数是 f 的参数。

【讨论】:

` 这通常是允许的` 不,不是,如果函数仅在返回类型上有所不同。这将违反 ODR,导致 UB 和灾难收据。 C++ will do overload resolution with those three arguments据我所知,论据是一样的,没有什么要解决的。 @SeverinPappadeux:“一般”是指“除非另有特定规则”。您是正确的,重载不涉及返回类型。这就是为什么我提出那个免责声明。这也是为什么我一直在写关于参数重载的文章。 @SeverinPappadeux:至于 ODR 违规,如果您在不同的命名空间中定义两个具有不同返回类型的函数,则完全不会出现这种情况。 (特别是这里的glm 命名空间)。 但是你所有的答案都围绕着重载分辨率——它显然不适用于这里。只要在相同的位置有相同的参数,重载就出窗了 @SeverinPappadeux lerp(dvec3, dvec3, double)template&lt;typename T&gt; glm::lerp(T, T, T) 是不同的重载。即使还有glm::lerp(dvec3, dvec3, double),也不违反ODR。【参考方案3】:

问题是因为 GLM 也定义了一个 lerp 函数,因此编译器也无法选择我所指的函数。

按照 cmets 的建议,解决方案是将我的函数从 lerp 重命名为 mlerp

我想这是一个很好的例子,说明为什么使用命名空间不是一个好主意。

【讨论】:

为什么这意味着使用命名空间不是一个好主意?如果您的 lerp 与 GLM 的函数位于不同的命名空间中,则它们不应冲突。 在你的代码中添加 using namespace 是个坏主意,因为避免这种情况正是命名空间存在的原因【参考方案4】:

这不是关于如何修复错误的答案,而是对您尝试给出您正在使用的方法名称的答案。

我建议你使用 std::unordered_map 来区分不同的功能。

std::unordered_map<void*, const char*> funcmap4;

union cast_fptr 
    dvec3 (*)(dvec3, dvec3, double) fptr;
    void *void_ptr;
;
#define MAP_VAL(FUNC_NAME) std::pair<void*, const char*>cast_fptr.fptr = &FUNC_NAME.void_ptr, #FUNC_NAME
funcmap.insert(MAP_VAL(lerp), MAP_VAL(plerp), MAP_VAL(splerp), MAP_VAL(ilerp));
#undef MAP_VAL

inline const char* erpToString(dvec3 (*f)(dvec3, dvec3, double)) noexcept 
    return funcmap[f];

【讨论】:

以上是关于如何在 C++ 中传递函数指针并在函数中进行比较?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 传递指针给函数

如何将函数指针传递给成员函数c++?

提升 C++。将带有签名指针的成员函数指针传递给函数

C++函数如何传递字符串?

C++成员函数指针指向全局函数指针

在 C++ 中传递函数指针