C ++中函数指针的隐式类型转换?

时间:2016-11-22 16:38:07

标签: c++ c++11

我有两个课程:Child来自Parent

#include <stdint.h>
#include <iostream>

using namespace std;

class Parent
{
public:
    int parentMember;
};

class Child : public Parent
{
};

现在,我有一个用于自定义实现动态数组的类模板(跳过不必要的部分)

template <typename T>
class DArray
{
private:
    T* m_array;
    int32_t m_length;
public:

    // Default constructor
    DArray() : m_array{ nullptr }, m_length{ 0 } {
    };

    // Search returns index of the first found item or -1 if not found, comparison is done 
    // using function pointer, which should return boolean
    int32_t Search(const T& data, bool(*comparisonFunction)(T, T)) {
        for (int32_t i = 0; i < m_length; i++) {
            if (comparisonFunction(m_array[i], data))
                return i;
        }
        return -1;
    }
};

我有一个比较函数,用于查明我的动态数组是否已包含具有相同值parentMember的元素

bool comparisonFunction(Parent* n1, Parent* n2) {
    return (n1->parentMember == n2->parentMember);
}

最后,我有我的动态数组,它应该保存指向Child个对象的指针。

int main()
{
    DArray<Child*> dArray;
    Child *c;
    dArray.Search(c, comparisonFunction);
    return 0;
}

此代码在此行返回错误:

dArray.Search(c, comparisonFunction);

错误是:

argument of type "bool (*)(Parent *n1, Parent *n2)" is incompatible with
parameter of type "bool (*)(Child *, Child *)"

我的问题是:为什么编译器不会隐式地将Child*转换为Parent*,因为当我将Child*作为参数传递给一个带{{1}的函数时作为参数?

如果没有为每个子类实现新的比较函数,有没有办法解决这个问题?

3 个答案:

答案 0 :(得分:3)

指针与函数类型之间没有隐式转换。

我会将Search函数更改为模板函数,该函数可以采用任何函子类型(包括lambdas,std::function等)。

template <typename F>
int32_t Search(const T& data, const F& comparisonFunction) {
    for (int32_t i = 0; i < m_length; i++) {
        if (comparisonFunction(m_array[i], data))
            return i;
    }
    return -1;
}

答案 1 :(得分:2)

Child *Parent *的隐式转换不一定是无操作。它可能涉及指针运算甚至条件(用于检查null)。因此,虽然可以调用一个函数,期望Parent *的参数类型为Child *,但这是唯一可能的,因为编译器会在调用点插入任何必要的转换代码。

这意味着虽然您可以Child * 转换为Parent *,但您无法直接将 Child *视为Parent *。但是,您的算法使用指向类型bool(Child*, Child*)的函数的指针。因此它会将两个Child *个对象传递给该函数。在通过函数指针调用的站点,编译器无法知道指针实际指向bool(Parent *, Parent*),因此它应该将转换代码从Child *插入到Parent *对于每个论点。

在传递指针的站点上也不能插入这样的代码。编译器实际上必须合成类型为bool(Child *, Child *)的包装器,将转换代码放入其中,并将指向该包装器的指针传递给Search。对于单个隐式转换而言,这将有点过于昂贵。

其他答案已经给出了问题的正确解决方案:从标准<algorithm>标题中获取灵感并接受任意仿函数而不是函数指针:

template <class F>
int32_t Search(const T& data, F comparisonFunction) {
    for (int32_t i = 0; i < m_length; i++) {
        if (comparisonFunction(m_array[i], data))
            return i;
    }
    return -1;
}

答案 2 :(得分:0)

编译器不会这样做。

通常最好将仿函数作为模板参数提交,以便它可以与可以在该位置编译的任何项一起使用。例如,std :: function和捕获lambda将使用模板参数,但不能使用显式函数指针声明。

template <typename T>
class DArray
{
private:
    T* m_array;
    int32_t m_length;
public:

    // Default constructor
    DArray() : m_array{ nullptr }, m_length{ 0 } {
    };

    // Search returns index of the first found item or -1 if not found, comparison is done 
    // using function pointer, which should return boolean
    template<typename COMPARE>
    int32_t Search(const T& data,COMPARE& compareFunction) {
        for (int32_t i = 0; i < m_length; i++) {
            if (compareFunction(m_array[i], data))
                return i;
        }
        return -1;
    }
};