Не удается вызвать функцию класса из класса функции шаблона внутри класса шаблона

#c #templates #design-patterns

#c #шаблоны #шаблоны проектирования

Вопрос:

Я пытаюсь создать шаблон, в котором абстрактный класс имеет несколько методов, где все получают один и тот же элемент. Производная реализация относится к определенному типу элемента, и шаблон должен исключить необходимость уменьшения типов.

Следующее не работает:

Предположим, что эти классы типов

 //--------------
// Data classes
//--------------
class TypeA
{
    virtual int a() = 0;
};

class TypeB : public TypeA
{
    int a() override
    {
        return 5;
    }
};
  

Шаблон:

 //-------------------
// Base action class.
//-------------------
class ExampleBase
{
public:
    virtual bool f1(TypeAamp; val) = 0;

    virtual bool f2(TypeAamp; val) = 0;
};

//----------------------------------
// Base specific Typed action class.
//----------------------------------
template<class T>
class ExampleSpecific1 : public ExampleBase
{
public:
    virtual bool specificF1(Tamp; specificVal) = 0;

    virtual bool specificF2(Tamp; specificVal) = 0;

    bool f1(TypeAamp; val) override
    {
        return fRunner<bool, specificF1>(val, false);
    }

    bool f2(TypeAamp; val) override
    {
        return fRunner<bool, specificF2>(val, false);
    }

private:

    // Run the specific function with the specific type
    template<class S, S (*pf)(Tamp; val)>
    S fRunner(TypeAamp; val, S defaultValue)
    {
        S ret = defaultValue;
        Tamp; specificVal = dynamic_cast<Tamp;>(val);

        if (amp;specificVal != nullptr) {
            ret = pf(specificVal);
        }

        return ret;
    }
};
  

Реализация

 //----------------------
// Class Implementation.
//----------------------
class ExampleImpl : public ExampleSpecific1<TypeB>
{
public:
    bool specificF1(TypeBamp; specificVal) override
    {
        // Do something
    }

    bool specificF2(TypeBamp; specificVal) override
    {
        // Do something
    }
};
  

Использование:

 //-----------
// Class Use.
//-----------
void main()
{
    ExampleImpl impl;
    TypeB myVal;

    TypeAamp; myBaseVal = myVal;
    impl.f1(myBaseVal);
    impl.f2(myBaseVal);
}
  

Я получаю следующую ошибку компиляции:

 error C2672: 'ExampleSpecific1<TypeB>::fRunner': no matching overloaded function found
note: while compiling class template member function 'bool ExampleSpecific1<TypeB>::f2(TypeA amp;)'
note: see reference to function template instantiation 'bool ExampleSpecific1<TypeB>::f2(TypeA amp;)' being compiled
note: see reference to class template instantiation 'ExampleSpecific1<TypeB>' being compiled
error C2975: 'pf': invalid template argument for 'ExampleSpecific1<TypeB>::fRunner', expected compile-time constant expression
note: see declaration of 'pf'
  

Шаблон функции, который работает (когда функция не находится внутри класса):

На основе предыдущего примера:

 template<class T, bool (*pf1)(Tamp; Val), bool (*pf2)(Tamp; Val)>
class ExampleSpecific2 : public ExampleBase
{
public:
    bool f1(TypeAamp; val) override
    {
        bool ret = false;
        Tamp; specificVal = dynamic_cast<Tamp;>(val);

        if (amp;specificVal != nullptr) {
            ret = pf1(specificVal);
        }
        return ret;
    }

    bool f2(TypeAamp; val) override
    {
        bool ret = false;
        Tamp; specificVal = dynamic_cast< Tamp;>(val);

        if (amp;specificVal != nullptr) {
            ret = pf2(specificVal);
        }
        return ret;
    }
};
  

Внешние функции:

 bool extF1(TypeBamp; val)
{
    // Do something.
}

bool extF2(TypeBamp; val)
{
    // Do something.
}
  

Использование:

 //-----------
// Class Use.
//-----------
void main()
{
    TypeB myVal;
    TypeAamp; myBaseVal = myVal;

    ExampleSpecific2<TypeB, extF1, extF2> impl2;

    impl2.f1(myBaseVal);
    impl2.f2(myBaseVal);
}
  

В примере, который не работает, я могу реализовать приведение вниз в каждой реализации, и тогда это работает, но это уродливо и не является универсальным. В рабочем примере я хочу, чтобы функция была во внутренней реализации класса, а не внешней по отношению к нему, это важно в более сложных сценариях, где базовый класс вызывает несколько производных методов.

  • Кстати, мне не нравится заголовок этого поста, если у вас есть лучшее предложение, которое мне нравится, это будет здорово.

Ответ №1:

Примечание :

  1. main должен возвращать значение int, а не void
  2. if (amp;specificVal != nullptr) всегда будет true, ссылка не может быть нулевой.

Я не понимаю, почему вы получили это сообщение об ошибке, с gcc я получил :

 no matching member function for call to 'fRunner'
  

Потому что pf тип был bool (ExampleSpecific1<TypeB>::*)(TypeB amp;) , который не совпадал с S (*pf)(Tamp; val) . Для первого нужен объект класса.

Поэтому я просто использую C 17 auto :

 template<class S, auto pf>
S fRunner(TypeAamp; val, S defaultValue){...}
  

Но вы можете использовать полный тип, если хотите.

Далее нам нужно вызвать функцию-член. Мне не нравится (и я не помню) синтаксис вызова функции-члена, поэтому я просто использую std::invoke из C 17. (смотрите:https://en.cppreference.com/w/cpp/utility/functional/invoke )

Live:https://wandbox.org/permlink/rEqgLSwSjEfqRK2o

 #include <iostream>
#include <vector>
//--------------
// Data classes
//--------------
class TypeA
{
    virtual int a() = 0;
};

class TypeB : public TypeA
{
    int a() override
    {
        return 5;
    }
};



//-------------------
// Base action class.
//-------------------
class ExampleBase
{
public:
    virtual bool f1(TypeAamp; val) = 0;

    virtual bool f2(TypeAamp; val) = 0;
};

//----------------------------------
// Base specific Typed action class.
//----------------------------------
template<class T>
class ExampleSpecific1 : public ExampleBase
{
private:

    // Run the specific function with the specific type
    template<class S, auto pf>
    S fRunner(TypeAamp; val, S defaultValue)
    {
        S ret = defaultValue;
        Tamp; specificVal = dynamic_cast<Tamp;>(val);
 
        ret = std::invoke(pf, *this, specificVal);


        return ret;
    }
public:
    virtual bool specificF1(Tamp; specificVal) = 0;

    virtual bool specificF2(Tamp; specificVal) = 0;

    bool f1(TypeAamp; val) override
    {
        return this->fRunner<bool, amp;ExampleSpecific1<T>::specificF1>(val, false);
    }

    bool f2(TypeAamp; val) override
    {
        return this->fRunner<bool, amp;ExampleSpecific1<T>::specificF2>(val, false);
    }


};

// Class Implementation.
//----------------------
class ExampleImpl : public ExampleSpecific1<TypeB>
{
public:
    bool specificF1(TypeBamp; ) override
    {
       std::cout << "specificF1" << std::endl;
       return true;
    }

    bool specificF2(TypeBamp; ) override
    {
        std::cout << "specificF2" << std::endl;
       return true;
    }
};



//-----------
// Class Use.
//-----------
int main()
{
    ExampleImpl impl;
    TypeB myVal;

    TypeAamp; myBaseVal = myVal;
    impl.f1(myBaseVal);
    impl.f2(myBaseVal);
}
  

До C 17:https://wandbox.org/permlink/HSGMy4zb4TgusESf

 template<class S, S (ExampleSpecific1<T>::*pf)(T amp;)> // full type since auto is C   17
S fRunner(TypeAamp; val, S defaultValue)
{
     S ret = defaultValue;
     Tamp; specificVal = dynamic_cast<Tamp;>(val);
     ret = (this->*pf)(specificVal); // Ugly pre 17 

     return ret;
}
  

Комментарии:

1. Это упрощенный пример, в реальной жизни основной функции вообще нет — это просто для демонстрационных целей, я годами не писал основную функцию, так что эта на мне. amp;specificVal != nullptr На самом деле это работает, проверял это много-много раз с failed dynaic_cast , и я не прошел мимо if инструкции (в любом случае, это не главное). Код работает идеально, когда методология fRunner включена непосредственно в реализацию f1 и f2 , как это сделано во втором примере ( ExampleSpecific2 ).

2. Из en.cppreference.com/w/cpp/language/dynamic_cast : » Если приведение завершается неудачей, а new-type является ссылочным типом, он выдает исключение, соответствующее обработчику типа std::bad_cast .». В общем, ссылка не может быть нулевой, если вы dynamic_cast указатель, тогда да, вы должны проверить

3. Исходя из этого, если мой ответ не соответствует вашим требованиям, можете ли вы привести живой пример? Я перестроил ваш код со всем, что было до «Я получаю следующую ошибку компиляции». И если это произойдет, пожалуйста, отметьте как разрешенный

4. С вашим кодом я получаю: note: 'auto' in non-type template parameters requires at least '/std:c 17' (Мы все еще в VS2017) — Поэтому я изменил auto на свое предыдущее определение и столкнулся с той же проблемой. Дело в том, что я получаю эту ошибку компиляции. Следовательно, единственный рабочий пример, который я могу привести, — это тот, где ‘fRunner’ не используется. Если это достаточно хорошо, я могу добавить это! Что касается dynamic_cast я проверю это, может быть, мы никогда не передаем класс, который нельзя понизить, и вы можете быть правы.

5. «Итак, я изменил auto на свое предыдущее определение». AFAIU, в чем проблема. У меня было решение до 17 без auto nor invoke . Скажите мне, работает ли это