【问题标题】:Errors Passing Template Arguments with Template Functions使用模板函数传递模板参数时出错
【发布时间】:2018-10-14 03:30:23
【问题描述】:

我在使用模板成员函数和模板变量时遇到问题。目标是为接口创建一个模板,该模板可以被继承并链接到在运行时初始化的一个或多个服务。该接口还将数据传递给这些专门的服务(例如 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

任何帮助理解模板的正确用法将不胜感激!

【问题讨论】:

  • { return m_AttachedService; } -> { return m_AttachedService&lt;T&gt;; }
  • 进行该更改后,我得到 4 个与相同内容相关的其他错误(例如,缺少 m_AttachedService 变量模板的参数,b 不是类型名称等)
  • 你还有很多问题。制定答案对我来说没有意义,因为我不清楚您要做什么。建议将在您的计算机上创建一个minimal reproducible example,直到您能够弄清楚如何解决构建错误。
  • 这正是我认为我正在做的事情。为简单起见,我省略了其他调用 Receive() 的代码。就我正在尝试做的事情而言,根据我上面描述的内容,目标是进一步扩展代码,以便可以使用此模板接口将数据传递给定义的任何专用服务,然后该服务可以对该数据进行操作。例如,我可以向 MyService 添加一个 doSomething() 方法,当新数据可用时,Receive() 可以调用该方法,该服务将对其进行操作。
  • 仍在寻求有关如何解决这些编译错误的任何帮助和指导。

标签: c++ function templates arguments member


【解决方案1】:

让我们从一个无需做任何有用的事情即可构建和运行的最小程序开始。

#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();
}

【讨论】:

  • 感谢您的彻底分解。不幸的是,我正在努力解决的部分是您最后省略的关键部分,即嵌套的 InterfaceListener 类。我已经通过专门针对我希望集成的每个服务来成功运行此接口代码。它试图重新格式化我遇到问题的顶级接口和侦听器类,我可能仍然没有掌握模板的正确使用。为了更清楚一点,这个嵌套的监听器将在接收到新数据时执行 Received(),但是这个数据只能在这个方法中访问,所以......
  • ..在此方法中,我必须评估数据,如果我希望服务(例如 MyService)对其进行操作,我当前会对其进行修改并将其存储在 m_AttachedService 的实例中。 (在运行时,目标是使用其接口和侦听器的实例来启动服务)。因此,虽然这两个实例都将在运行时实例化,但我试图引用听起来我必须提供一个实例的模板变量。我怎么做?在 main 中,我指定了服务实例,该实例又设置了我想从那里引用的接口实例和 m_AttachedService 实例。
  • 所以当我继续研究和学习时,我想我想要实现的是使用模板的依赖注入。 (还有嵌套类的细微差别,但我也受到限制)。正如之前提到的,这一切都是为了在类之间共享数据,我在接口方面受到了一些限制。
  • 依赖注入模式包含三个组件。一个客户端、一个或多个服务以及服务注入器。我没有在您发布的代码中看到这些内容。
  • 那是因为我刚开始的时候并没有想到。我需要 Receive() 才能向 MyService() 添加数据(例如任务),然后让这个链接的服务对其进行操作。由于 Receive() 很快结束,我需要一种方法来保存这些数据。它基本上将为任何派生类类型的 Service() 执行一组一致的操作,因此我希望有 1 个接口类支持任何派生服务。这就是我查看模板函数的原因。也许我可以使用 shared_ptrs 或为基本参数传递派生 ref(?),但在每种情况下,我都会遇到访问成员函数的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-10
  • 1970-01-01
相关资源
最近更新 更多