2016-11-22 1 views
3

У меня есть 2 классов: Child происходит от Parent:Неявное преобразование типов в указателях функций в C++?

#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* в качестве аргумента функции, которая принимает параметр Parent* в качестве параметра?

Есть ли способ решить эту проблему без реализации новой функции сравнения для каждого дочернего класса?

+0

какой-либо причине вы не используете ' std :: vector' и ''? –

+0

@appleapple Я все еще изучаю C++, и я хочу понять, как это работает «за занавеской» :) – Riko

+1

Лучше всего понять, как это сделать в первую очередь. –

ответ

0

Компилятор этого не сделает.

Как правило, лучше всего представить функторы в качестве параметров шаблона таким образом, чтобы он работал с любым элементом, который мог бы скомпилироваться в этом месте. Функция std :: и захват 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; 
    } 
}; 
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; 
} 
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; 
} 
+0

Спасибо за подробное объяснение, я отметил ответ, который был первым правильным. – Riko