【问题标题】:How to take template mixin as argument?如何将模板混合作为参数?
【发布时间】:2016-08-12 08:46:08
【问题描述】:

考虑到一个 C++ 模板 mixin 结构,我如何编写一个函数来接受一个带有特定组件的 mixin?在这个例子中,如何将withAandBworksWithA()

struct Base {};

template <class T>
struct HasA : T
{
    int A;
};

template <class T>
struct HasB : T
{
    int B;
};

void WorksWithA(HasA<Base> &p)
{
    p.A++;
}

void WorksWithAandB(HasA<HasB<Base> > &p)
{
    p.A++;
    p.B++;
}

int _tmain(int argc, _TCHAR *argv[])
{
    HasA<Base> withA;
    HasA<HasB<Base> > withAandB;

    WorksWithA(withA); // OK
    WorksWithAandB(withAandB); // OK
    WorksWithA(withAandB); // KO, no conversion available

    return 0;
}

即使抛开构造问题或 mixin 排序问题(HasA&lt;HasB&lt;Base&gt;&gt;HasB&lt;HasA&lt;Base&gt;&gt;),除了将其设为模板之外,我看不到编写此函数的好方法。

我目前处于没有 C++11 的环境中,但如果现代 C++ 提供解决方案,我会很感兴趣。

非常感谢!

【问题讨论】:

    标签: c++ templates mixins


    【解决方案1】:

    您可以将WorksWithA 设为模板函数,该函数接受使用HasA 包装的任何类:

    template<typename T>
    void WorksWithA(HasA<T> &p)
    {
      p.A++;
    }
    

    在这种情况下,您的代码编译时没有错误。

    【讨论】:

      【解决方案2】:

      我认为您也必须制作函数模板?

      template <class T>
      void WorksWithA(HasA<T> &p)
      {
          p.A++;
      }
      
      template <class T>
      void WorksWithAandB(HasA<HasB<T> > &p)
      {
          p.A++;
          p.B++;
      }
      

      因为HasA&lt;HasB&lt;Base&gt;&gt; 无法转换为HasA&lt;Base&gt;

      【讨论】:

      • 好的,谢谢!我希望由于HasB&lt;Base&gt; 是从Base 派生的,所以有一种方法可以将HasA&lt;HasB&lt;Base&gt;&gt; 转换为HasA&lt;Base&gt;
      • 嘿,我必须更正我的答案,因为就像你说的那样,HasB&lt;Base&gt; 派生自 Base 所以它是可转换的,但 HasA&lt;HasB&lt;Base&gt;&gt; 确实无法转换为 HasA&lt;Base&gt;,因为这些2种没有关系。所有实例化的模板类型都是不同的,你可以看到它们就像 HasA 和 HasA,虽然 X 和 Y 有关系,但 HasA 和 Has 完全不同。
      【解决方案3】:

      正如其他人所指出的,这些函数作为模板可能会更好。但是,当您使用它时,您还可以删除一些代码重复:

      template<class B> 
      void WorksWithA(HasA<B> &p) 
      { 
          p.A++; 
      } 
      
      template<class B> 
      void WorksWithB(HasA<B> &p) 
      { 
          p.B++;
      }
      
      template<class Has>
      void WorksWithAandB(Has &p)
      {
          WorksWithA(p);
          WorksWithB(p);
      }
      

      这里WorksWithAAndB 调用其他类型。因此:

      1. 您正在重用代码。
      2. 您正在重用编译器对传递给WorksWithAWorksWithB 的类型类型的检查,以检查传递给WorksWithAAndB 的类型。

      【讨论】:

        【解决方案4】:

        您必须将WorksWithA 设为函数模板;没有其他办法。想一想:WorksWithA 与具有 HasA 混合的 any 类型一起使用。这只能用模板来表达。

        【讨论】:

          【解决方案5】:

          另外,如果HasAHasB有接口的意思,可以考虑使用继承来解决这个问题:

          struct HasA
          {
            int A;
            virtual ~HasA() = default;
          };
          
          struct HasB
          {
            int B;
            virtual ~HasB() = default;
          };
          
          struct HasAB : HasA, HasB
          {
          };
          
          struct Base : HasAB
          {};
          
          void WorksWithA(HasA &p)
          {
            p.A++;
          }
          
          void WorksWithAandB(HasAB &p)
          {
            p.A++;
            p.B++;
          }
          

          在这里你可以用任何实现WithA接口的类的对象调用WorkWithA,用任何实现HasAB接口的类的对象调用WorksWithAandB

          PS:不幸的是,在这个例子中,如果没有从 HasAB 继承,就不可能使用实现 HasAHasB 的类对象调用 WorksWithAandB,但这可以使用模板和 SFINAE 解决。

          【讨论】:

          • 谢谢,这实际上是我目前的设计,我正在寻找一种更好的方式来拥有这个“HaveAB”。我将研究 SFINAE 的概念。
          猜你喜欢
          • 2016-06-24
          • 2011-09-14
          • 2021-12-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-08
          相关资源
          最近更新 更多