Ошибки при передаче аргументов шаблона с функциями шаблона - PullRequest
0 голосов
/ 03 мая 2018

У меня проблемы с использованием функций-членов шаблона и переменных шаблона. Цель состоит в том, чтобы создать шаблон для интерфейса, который может быть унаследован и связан с одной или несколькими службами, инициализированными во время выполнения. Этот интерфейс также будет передавать данные в эти специализированные службы (например, MyService). Для более простого примера с этим кодом:

#include <string>

class Interface
{
public:
    template <class T> bool init(T *_Service) {AttachService(*_Service);}  //This function does other stuff too.
    template <typename T> void AttachService(T _Service) { m_AttachedService<T> = *_Service; }
    template <typename T> T AttachedService() { return m_AttachedService; }
protected:
    template<typename T> static T m_AttachedService;

    class InterfaceListener
    {
        void Received()
        {
            int a = 1;
            std::string b = "hello";
            AttachedService().setA(a);
            m_AttachedService.setB(b);
        };
    };
};

class Service
{
    Service();
    ~Service();
    virtual void init() = 0;
};

class MyService : public Service, public Interface
{
    MyService();
    ~MyService();
    private:
        int A;
        std::string B;
    protected:
        Interface x;
    public:
        void init() { x.init(this);}
        void setA(int a) { A = a; }
        void setB(std::string b) { B = b; }
};

int main();
{
    MyService myserv;
    myserv.init();
}

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

C2672: 'Interface::AttachedService': no matching overloaded function found
C2783:  'T Interface::AttachedService(void)': could not deduce template argument for 'T'
C2228:  left of '.setA' must have class/struct/union
C3245:  'Interface::m_AttachedService': use of a variable template requires template argument list

Буду признателен за любую помощь в понимании правильного использования шаблонов!

1 Ответ

0 голосов
/ 07 мая 2018

Давайте начнем с минимальной программы, которая собирает и запускает, не делая ничего полезного.

#include <string>

class Interface
{
};

class Service
{
   protected:
      Service() {}
      ~Service() {}
      virtual void init() = 0;
};

class MyService : public Service, public Interface
{
   private:
      int A;
      std::string B;
   protected:
      Interface x;
   public:
      MyService() {}
      ~MyService() {}
      void init() {}
      void setA(int a) { A = a; }
      void setB(std::string b) { B = b; }
};

int main()
{
   MyService myserv;
   myserv.init();
}

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

  1. Interface пусто.
  2. Функции-члены Service находятся в разделе public. Конструктор по умолчанию и деструктор Service имеют пустую реализацию.
  3. Конструктор по умолчанию и деструктор MyService находятся в разделе public класса и имеют пустые реализации.
  4. MyService::init() имеет пустую реализацию.

Теперь мы можем начать добавлять больше кода.

Я изменил MyService::init() на:

  void init() { x.init(this); }

Без других изменений я получил следующую ошибку компилятора.

socc.cc: In member function ‘virtual void MyService::init()’:
socc.cc:25:23: error: ‘class Interface’ has no member named ‘init’
       void init() { x.init(this);}

Теперь Interface необходимо обновить с помощью функции init.

Я добавил фиктивную реализацию, чтобы продвинуть процесс вперед.

class Interface
{
   public:
      template <class T> bool init(T *_Service) { return true; }
};

Время добавить что-нибудь полезное к Interface::init(). Меняя его на

template <class T> bool init(T *_Service)
{
   AttachService(*_Service); 
   return true; 
}

выдает следующую ошибку компилятора, что неудивительно.

socc.cc: In instantiation of ‘bool Interface::init(T*) [with T = MyService]’:
socc.cc:31:32:   required from here
socc.cc:8:23: error: ‘AttachService’ was not declared in this scope
          AttachService(*_Service);
          ~~~~~~~~~~~~~^~~~~~~~~~~

Время добавить AttachService. Изменение Interface на (закрытие совпадает с тем, что у вас есть)

class Interface
{
   public:
      template <class T> bool init(T *_Service)
      {
         AttachService(*_Service); 
         return true; 
      }

      template <typename T> void AttachService(T _Service)
      {
         m_AttachedService<T> = *_Service;
      }

   protected:
      template<typename T> static T m_AttachedService;
};

выдает следующую ошибку компилятора.

socc.cc: In instantiation of ‘void Interface::AttachService(T) [with T = MyService]’:
socc.cc:8:10:   required from ‘bool Interface::init(T*) [with T = MyService]’
socc.cc:39:32:   required from here
socc.cc:14:33: error: no match for ‘operator*’ (operand type is ‘MyService’)
          m_AttachedService<T> = *_Service;
                                 ^~~~~~~~~

Это имеет смысл. В AttachServie, _Service не является указателем.

Изменение Inteface::AttachService на:

  template <typename T> void AttachService(T _Service)
  {
     m_AttachedService<T> = _Service;
  }

устраняет ошибку компилятора, но есть ошибка компоновщика.

:socc.cc:(.rdata$.refptr._ZN9Interface17m_AttachedServiceI9MyServiceEE[.refptr._ZN9Interface17m_AttachedServiceI9MyServiceEE]+0x0): undefined reference to `Interface::m_AttachedService<MyService>'
collect2: error: ld returned 1 exit status

Это имеет смысл, поскольку мы не определили переменную-член static.

Добавление следующей строки

template<typename T> T Interface::m_AttachedService;

сразу после определения Interface устраняет ошибку компоновщика.

Ниже приведена следующая версия полной программы, которая успешно собирается и работает, даже если она по-прежнему не помогает.

#include <string>

class Interface
{
   public:
      template <class T> bool init(T *_Service)
      {
         AttachService(*_Service); 
         return true; 
      }

      template <typename T> void AttachService(T _Service)
      {
         m_AttachedService<T> = _Service;
      }

   protected:
      template<typename T> static T m_AttachedService;
};

template<typename T> T Interface::m_AttachedService;

class Service
{
   protected:
      Service() {}
      ~Service() {}
      virtual void init() = 0;
};

class MyService : public Service, public Interface
{
   private:
      int A;
      std::string B;
   protected:
      Interface x;
   public:
      MyService() {}
      ~MyService() {}
      void init() { x.init(this); }
      void setA(int a) { A = a; }
      void setB(std::string b) { B = b; }
};

int main()
{
   MyService myserv;
   myserv.init();
}

Время добавить вашу версию AttachedService в Interface

template <typename T> T AttachedService() { return m_AttachedService; }

Это приводит к следующей ошибке компилятора.

socc.cc: In member function ‘T Interface::AttachedService()’:
socc.cc:17:73: error: missing template arguments before ‘;’ token
     template <typename T> T AttachedService() { return m_AttachedService; }

Это имеет смысл, поскольку m_AttachedService является не переменной-членом, а шаблоном переменной-члена.

Изменив это на

template <typename T> T AttachedService() { return m_AttachedService<T>; }

удаляет эту ошибку.


Теперь последний кусок в Interface. Вложенный класс InterfaceListener, который вы опубликовали, звучит правильно. У вас есть

  class InterfaceListener
  {
     void Received()
     {
        int a = 1;
        std::string b = "hello";
        AttachedService().setA(a);
        m_AttachedService.setB(b);
     };
  };

Проблемы в этом классе:

  1. AttachedService() неправильно, так как это шаблон функции-члена. Вы должны предоставить параметр шаблона, чтобы использовать его.
  2. Кроме того, AttachedService() не является static функцией-членом. Вам нужен экземпляр Interface, чтобы сделать этот вызов.
  3. m_AttachedService не является переменной-членом. Это шаблон переменной члена. Вы должны предоставить параметр шаблона, чтобы использовать его.
  4. Функции setA() и setB() действительны, только если параметром шаблона является MyService. Не имеет смысла иметь в этой функции код, специфичный для типа.

Я оставлю вам на размышление над тем, как вы собираетесь использовать InterfaceListener, и определите его функции соответствующим образом. До тех пор у меня собирается и запускается следующая программа.

#include <string>

class Interface
{
   public:
      template <class T> bool init(T *_Service)
      {
         AttachService(*_Service); 
         return true; 
      }

      template <typename T> void AttachService(T _Service)
      {
         m_AttachedService<T> = _Service;
      }

      template <typename T> T AttachedService() { return m_AttachedService<T>; }

   protected:
      template<typename T> static T m_AttachedService;

};

template<typename T> T Interface::m_AttachedService;

class Service
{
   protected:
      Service() {}
      ~Service() {}
      virtual void init() = 0;
};

class MyService : public Service, public Interface
{
   private:
      int A;
      std::string B;
   protected:
      Interface x;
   public:
      MyService() {}
      ~MyService() {}
      void init() { x.init(this); }
      void setA(int a) { A = a; }
      void setB(std::string b) { B = b; }
};

int main()
{
   MyService myserv;
   myserv.init();
}
...