【问题标题】:Bypass virtual template functions to achieve wished result绕过虚拟模板功能以达到预期的结果
【发布时间】:2013-08-27 21:56:25
【问题描述】:

由于 C++ 语言限制(不支持模板化的虚函数),我的实际想法没有编译。也许你们中的一些人对以下代码 sn-p 有一些设计建议。

我想对不同类型的输入运行不同的算法。例如,我有一个积分图像和一个灰度图像作为输入。积分图像需要 32 位像素,而我的灰度图像需要 8 位(仅作为示例)。因此,我有两个频道:CChannel<uint8>CChannel<uint32>

因为我可以为单个图像有多个通道,所以我将通道存储在一个向量中,std::vector<CChannelBase*>...这就是 CChannelBase 类的理由。

class CChannelBase
{
public:
    virtual ~CChannelBase( void ) = 0;
};

template <class ValueType>
class CChannel : public CChannelBase
{
public:
    typedef ValueType value_type_t;
    Channel(): m_data_p(0) {}
    void setData(ValueType* f_data_p) { m_data_p = f_data_p; }
    const ValueType getData( void ) { return m_data_p; }
private:
    ValueType* m_data_p;
};

我所有的算法都实现了一个接口,并且必须与每个图像通道兼容。

class IAlgorithmInterface
{
public:
    virtual ~IAlgorithmInterface() = 0;
    template <class ValueType>
    virtual void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r);
};

class CAlgorithmA : IAlgorithmInterface
{
    CAlgorithmA() {...};
    ~CAlgorithmA() {...};
    template <class ValueType>
    void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r) {...};
};

class CAlgorithmB : IAlgorithmInterface
{
    CAlgorithmB() {...};
    ~CAlgorithmB() {...};
    template <class ValueType>
    void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r) {...};
};

当然,这段代码没有被编译,因为我们有虚拟模板函数。无论如何,我正在寻找一个很好的设计来克服这个问题。一个解决方案是所有类(IAlgorithmInterfaceCAlgorithmACAlgorithmB)都被模板化,这是我不想做的事情。我在这里看到了一些帖子,其中推荐了访问者模式。但老实说,我不知道如何在我的情况下使用它。

【问题讨论】:

  • 将虚拟模板称为“缺少的语言功能”就像说您作为明星密码分析员的职业目前由于生活中缺少恒定时间素因数分解功能而被搁置。
  • 你是说每个算法都需要能够处理多种不同的值类型,但它们总是以完全相同的方式处理?
  • 是的,但是之前不知道图像数组的指针类型。当然,我可以重新解释给定的指针,但随后会出现问题。 @kerreksb:再次阅读我的文字:)
  • 我们没有模板化的虚函数有一个很好的理由:它们没有意义。 virtual 表示“通过继承实现”,而 template 表示“继承实现”。您在这里真正想要的是特征和函子,例如 stl 使用。看看 STL 的分配器是如何工作的,这应该可以帮助您开始解决您以 C++ 方式描述的问题。
  • 唯一可行的方法是写下 ValueType 的所有可能类型。 IE。让你的 doWork 函数接受一个变体 对象。这相当于编写了多个 doWork 重载(但也许变体的访问者会节省一些锅炉代码)

标签: c++ templates design-patterns virtual


【解决方案1】:

不确定这是否能解决您的所有问题,因为您实际上希望此代码实际“执行”的操作并没有太多可做的事情,但是稍微改组一下,它就可以编译:

typedef float float32_t;

class CChannelBase
{
public:
    virtual ~CChannelBase( void ) = 0;
};

template <class ValueType>
class CChannel : public CChannelBase
{
public:
    typedef ValueType value_type_t;
    CChannel(): m_data_p(0) {}
    void setData(ValueType* f_data_p) { m_data_p = f_data_p; }
    const ValueType getData( void ) { return m_data_p; }
private:
    ValueType* m_data_p;
};

template <class ValueType>
class IAlgorithmInterface
{
public:
    virtual ~IAlgorithmInterface() = 0;
    virtual void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r);
};

template <class ValueType>
class CAlgorithmA : IAlgorithmInterface<ValueType>
{
    CAlgorithmA() {};
    ~CAlgorithmA() {};
    void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r) {};
};

template <class ValueType>
class CAlgorithmB : IAlgorithmInterface<ValueType>
{
    CAlgorithmB() {};
    ~CAlgorithmB() {};
    void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r) {};
};


int main()
{
}

本质上,只是移动templace&lt;class ValueType&gt; out a level, to the class, and add it to theIAloorithmInferface`继承。

我完全希望被告知这不适用于您尝试做的事情,但是没有示例说明您实际上希望这些课程做什么,所以我无法确定它是否有效.

【讨论】:

  • 感谢您的回答。我已经在我的帖子中写道,我想避免这种(有效的)解决方案。
  • 但是您仍然想为 ValueType 使用模板,是吗?公平地说,我不完全确定你是如何看待这种工作的。您不能拥有一个具有一个虚函数的类,它采用多种类型。 doWork 函数将占用 vtable 中的一个槽,供调用代码使用。你不能真正用不同的参数调用同一个函数,因为被调用函数需要知道参数是什么。所以我只是不明白编译器应该如何弄清楚如何做到这一点。但是我想你希望编译器能神奇地解决问题......
  • 不,我不认为有什么魔法,我知道这个问题:) 我只是在寻找一种方法来避免模板化整个班级。但是,如果没有其他好的方法可以克服,我必须这样做。
  • 那么,您希望它究竟如何工作?编译器知道你想做什么,并相应地修复 vtable?请记住,对于具有虚函数的类,每个类都有一个 vtable [除非你有多重继承]。因此,如果您有不同的类型(CChannel&lt;ValueType&gt;ValueType 的不同类型),这将如何解决?显然,如果ValueType 不是太多不同的类型,我们可以实例化每个类中的所有变体。
  • 让我玩一下 - 我认为可能有办法解决它 - 但你仍然需要做一些工作才能涵盖所有不同的像素格式。
【解决方案2】:

好的,这有点令人费解,但您有一个令人费解的要求。 IAlgorithmInterface 可以有模板方法,但不能是虚拟的。您可以创建一个中间类,该类本身是一个从IAlgorithmInterface 虚拟派生的模板,它将模板方法代理到一些执行实际工作的代码。真正的工作是在模板参数中提供给IAlgorithmInterface 的模板派生。

此方案允许IAlgorithmInterface 的模板方法通过向下转换调度到适当的派生类。

class IAlgorithmInterface
{
public:
    virtual ~IAlgorithmInterface() {}

    template <class ValueType>
    void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r);    
};

template <class ValueType, typename RealWork>
class IAlgorithmTemplate : virtual public IAlgorithmInterface
{
public:
    void doWork(const CChannel<ValueType>* f_channel_p, float32_t& f_result_r) {
        RealWork()(f_channel_p, f_result_r);
    }
};

template <class ValueType>
void IAlgorithmInterface::doWork(const CChannel<ValueType>* f_channel_p,
                                 float32_t& f_result_r)
{
    IAlgorithmTemplate<ValueType> *alg
        = dynamic_cast<IAlgorithmTemplate<ValueType>*>(this);
    alg->doWork(f_channel_p, f_result_r);
}

现在,通过多重继承,我们可以创建算法实现将使用的实际接口。因为IAlgorithmTemplate使用虚拟继承,所以IAlgorithmInterface实例只有一个。所以要支持ValueTypeNew,你需要在继承列表中添加一个IAlgorithmTempalte&lt;ValueTypeNew&gt;

template <template <class> class RealWork>
class IAlgorithmBase :
    public IAlgorithmTemplate<ValueTypeOne, RealWork<ValueTypeOne> >,
    public IAlgorithmTemplate<ValueTypeTwo, RealWork<ValueTypeTwo> >,
    //...
    public IAlgorithmTemplate<ValueTypeLast, RealWork<ValueTypeLast> > {
};

最后,每个算法都派生自IAlgorithmBase,并将RealWork作为模板实现。

template <class ValueType>
struct RealAlgorithmA {
    void operator () (const CChannel<ValueType>* f_channel_p, float32_t& f_result_r) {
        //...
    }
};

class CAlgorithmA : public IAlgorithmBase<RealAlgorithmA>
{
public:
    CAlgorithmA() {...}
    ~CAlgorithmA() {...}
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-08
    • 2015-01-04
    • 1970-01-01
    • 2010-10-19
    • 2020-06-19
    • 2011-06-25
    相关资源
    最近更新 更多