【问题标题】:Specifying an interface common to class template specializations指定类模板特化通用的接口
【发布时间】:2017-12-26 10:38:46
【问题描述】:

假设一些类模板特化有一些公共的公共接口。是否可以只声明一次?

例子:

// Example for a generator
class G
{
public:
    double generate(int channel);
};

template <typename Generator>
class A
{
public:
    double step();
protected:
    Generator g;
};

template <typename Generator, bool fixedMono>
class B;

template <typename Generator>
class B<Generator,true> : public A<Generator>
{
};

template <typename Generator>
class B<Generator,false> : public A<Generator>
{
public:
    void setNumChannels(int numChannels);
private:
    int numChannels;
};

template<typename Generator>
double B<Generator,true>::step() { return A<Generator>::g.generate(0); }

template<typename Generator>
double B<Generator,false>::step()
{
    double sum = 0;
    for (int i = 0; i < numChannels; ++i)
        sum += A<Generator>::g.generate(i);
    return sum;
}

这会失败,因为编译器无法识别在 B 特化中声明的 step(事实上,它不是)。

在一个非玩具示例中,公共接口可能大于单个函数,并且在所有专业化中重复其声明是不可取的。

有没有只指定一次通用接口的好方法?

请注意重构上述示例的建议,因此模板特化不需要有一个公共接口是无关紧要的,除非重构方法是一种总是可以用来消除此类接口的方法。问题是,在需要时只声明一次这样的通用接口在技术上是否可行。

【问题讨论】:

  • 您在寻找类型特征吗?
  • 在我看来,func 在这两个专业中除了名称之外的任何东西都不常见。只需在每个中声明即可。您已经尝试为每个定义它。
  • @StoryTeller 这很常见。玩具示例实现可能无法很好地反映它。
  • 玩具示例仅指定了一个通用名称。如果有共同行为,您可以随时将其提取到其他成员函数中。在A 中定义那些。然后在每个B 专业化中的func 的每个定义中使用它们。如果这仍然不能满足......好吧,一个更好的例子肯定是为了。
  • @StoryTeller 改进了示例。

标签: c++ templates template-specialization


【解决方案1】:

一种改进是使用 CRTP。就其本身而言,它并没有消除两次声明成员函数的需要,在每个特化中一次,但至少重复声明是私有实现成员函数,而不是只声明一次的公共接口。

// Example for a generator
class G
{
public:
    double generate(int channel);
};

template <typename Derived, typename Generator>
class A
{
public:
    double step() { return static_cast<Derived&>(*this)->stepImpl(); };
protected:
    Generator g;
};

template <typename Generator, bool fixedMono>
class B;

template <typename Generator>
class B<Generator,true> : public A<B<Generator,true>, Generator>
{
private:
    double stepImpl();
    using A<B<Generator,true>, Generator>::g;
};

template <typename Generator>
class B<Generator,false> : public A<B<Generator,false>, Generator>
{
public:
    void setNumChannels(int numChannels);
private:
    double stepImpl();
    int numChannels;
    using A<B<Generator,false>, Generator>::g;
};

template<typename Generator>
double B<Generator,true>::stepImpl() { return g.generate(0); }

template<typename Generator>
double B<Generator,false>::stepImpl()
{
    double sum = 0;
    for (int i = 0; i < numChannels; ++i)
        sum += g.generate(i);
    return sum;
}

【讨论】:

    【解决方案2】:

    如果你想在一个类中定义一个方法,你必须首先在同一个类中声明它。继承允许共享行为,但在这里,您无论如何都要重新实现整个方法。

    【讨论】:

      【解决方案3】:

      主要问题是您无法做到这一点,因为这是不允许的。 一个很好的解释:https://stackoverflow.com/a/41578586/2504757

      下面是另一种方式。使用Curiously recurring template pattern

      为默认模板参数定义一个空类

      //==================================================================
      class NoNumChannels
      {};
      

      走出踏入自己的班级。

      //==================================================================
      template< typename Generator, typename NumChannels = NoNumChannels >
      class Stepper;
      
      template< typename Generator >
      class Stepper< Generator, NoNumChannels > // Specialization case when No Numchannels. Just use the generator.
      {
      public:
          double step()
          {
              return g.generate( 0 );
          }
      
      protected:
          Generator g;
      };
      
      template< typename Generator, typename NumChannels > // Otherwise use a class which has NumChannels
      class Stepper
      {
      public:
          double step()
          {
              double sum = 0;
              for (int i = 0; i < (static_cast<NumChannels&>(*this).numChannels_v()); ++i) //Use Curiously recurring template pattern to access num of channels
                  sum += g.generate( i );
              return sum;
          }
      
      protected:
          Generator g;
      };
      

      B 的定义稍作修改。对于没有 NumChannels 的情况,只需从带有 Generator 的 Stepper 继承即可。否则从带有 Generator 和 self 的 Stepper 继承。

      //==================================================================
      template< typename Generator, bool fixedMono >
      class B;
      
      template< typename Generator >
      class B< Generator, true > : public Stepper< Generator >
      {};
      
      template< typename Generator >
      class B< Generator, false > : public Stepper< Generator, B< Generator, false > >
      {
      public:
          void setNumChannels( int numChannels );
          int numChannels_v();
      private:
          int numChannels;
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-10
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        相关资源
        最近更新 更多