指定类模板特化

时间:2017-12-26 10:38:47

标签: c++ templates template-specialization

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

示例:

// 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;
}

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

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

有没有很好的方法只指定一次公共接口?

请注意重构上述示例的建议,因此除非重构方法总是用于消除此类接口,否则模板专业化不需要具有公共接口无关紧要。问题是,在需要的情况下,是否只需要声明一次这样的通用接口在技术上是可行的。

3 个答案:

答案 0 :(得分:0)

一个改进是使用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;
}

答案 1 :(得分:0)

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

答案 2 :(得分:0)

主要问题是你无法实现这一点,因为它是不允许的。 一个很好的探索: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的情况,只需继承Stepper with Generator。否则继承自Stepper with Generator和self。

//==================================================================
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;
};