使用多态函数覆盖模板化函数

时间:2011-10-14 09:21:26

标签: c++ templates polymorphism template-specialization

如果我有

template<class T>
TalkyBuffer& operator<<(T const &object) { // Template
...
}
TalkyBuffer& operator<<(TalkySerialisable const &object); // Override

和一个班级

class A : public TalkySerialisable {
...}

然后,如果我执行

TalkyBuffer b;
A test;
b << test;

然后gcc调用Template函数而不是Override函数

但是,如果我专门定义覆盖

TalkyBuffer& operator<<(A const &object); // Override without polymorphism

然后gcc选择那一个。 是否有一种用抽象类覆盖模板化函数的实用方法?

我读过这篇文章,但它没有说明当你将多态性投入混合时会发生什么: http://www.gotw.ca/publications/mill17.htm 此外,我在这里找不到解决方案,但也许我使用了错误的术语。

3 个答案:

答案 0 :(得分:1)

定义函数TalkyBuffer& operator<<(TalkySerialisable const &object);时,您没有覆盖。你正在超载tmeplated功能。

但是,当编译器看到b << test;时,它会搜索想要A的运算符。它有一个,它是模板化的功能,不需要自动转换。这是最好的选择。

重载函数需要在参数上自动转换(从A到TalkySerialisable)以适合声明,并且不是最佳选择。

答案 1 :(得分:1)

我认为可以使用一个简单的function解决方案,重用函数重载来推导。

struct specialized {};
struct generic {};

template <class T>
TalkyBuffer& serialize(TalkyBuffer& buffer, T const& object, generic) {
  ...
}

generic dispatch(...) {} // always picked up last in overload resolution

template <class T>
TalkyBuffer& TalkyBuffer::operator<<(T const& object) { // Template
  return serialize(*this, object, dispatch(object));
}

现在,让我们实现您的自定义类:

TalkyBuffer& serialize(TalkyBuffer& buffer,
                       TalkySerialisable const& object,
                       specialized);

specialized dispatch(TalkySerialisable const&) {}    

创建派生的:

class A: public TalkySerialisable {};

那么,会发生什么?

  • TalkyBuffer::operator<<(T const&)将被选中
  • 在尝试解决serialize的重载时,会首先计算dispatch的结果
  • 在解析dispatch的结果时,dispatch(TalkySerializable const&)dispath(...)匹配得更好,因此返回类型为specialized
  • 无法使用通用serialize(没有从specialized转换为generic),因此继承就会启动

答案 2 :(得分:0)

使用Boost.enable_if的解决方案:

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_of.hpp>

template<typename T>
typename boost::disable_if<
    boost::is_base_of<TalkySerializable, T>,
    TalkyBuffer &
>::type operator<<(T const & object) { // Template for non TalkySerializable
...
}

template <typename T>
typename boost::enable_if<
    boost::is_base_of<TalkySerializable, T>,
    TalkyBuffer &
>::type operator<<(T const & object); // Template overload for TalkySerializable

...

TalkyBuffer b;
A test;
b << test; // calls operator<< <A>(A const &), which instantiates 
           // the overload for TalkySerializable
b << 41; // calls operator<< <int>(int const &), which corresponds to
         // the "default" overload

我不确定这是最好的解决方案,但我找不到更好的解决方案:专门化模板也不起作用。


正如@Matthieu在评论中指出的那样,之前的解决方案的主要缺点是基本模板需要知道它将被重载,这是一种不必要的耦合,阻碍了可扩展性。

为了解决这个问题,我提出了一种使用tag dispatching的新方法,以及使用Boost.MPL macros的特征类和编译时内省。

// TalkyBuffer.hpp

#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/has_xxx.hpp>

// defines a metafunction has_talky_buffer_tag<T> that allows us to know at
// compile-time if T has a member type named talky_buffer_tag
BOOST_MPL_HAS_XXX_TRAIT_DEF(talky_buffer_tag)

// tag for the default case
struct default_talky_buffer_tag {};

// trait class for obtaining the tag of a type
template <typename T, typename Enable = void >
struct talky_buffer_trait
{
    typedef default_talky_buffer_tag type;
};

// specialization for types that provide a nested typedef
template <typename T>
struct talky_buffer_trait<T, 
    typename boost::enable_if<has_talky_buffer_tag<T> >::type>
{
    typedef typename T::talky_buffer_tag type;
};


struct TalkyBuffer 
{
    // Insertion operator, which calls an implementation function that can
    // be overloaded depending on the tag
    template<typename T>
    TalkyBuffer & operator<<(T const & object) 
    {
        typename talky_buffer_trait<T>::type tag;
        return insertionOperatorImpl(*this, object, tag);
    }
};

// default implementation
template <typename T>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, T const & object,
    default_talky_buffer_tag)
{
    std::cout << "default";
    return buf;
}


//-------
// TalkySerializable.hpp

struct TalkySerializable 
{ 
    struct tag {}; 
    typedef tag talky_buffer_tag; 
};

// A inherits from the nested typedef
struct A : public TalkySerializable {};

// implementation for TalkySerializable objects
template <typename Serializable>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, Serializable const & object,
    TalkySerializable::tag)
{
    std::cout << "specialized";
    return buf;
}


//-------
int main()
{
    TalkyBuffer b;
    A test;
    b << test; // outputs "specialized"
    b << 41;   // outputs "default"
}

要为给定类型T提供插入运算符的新实现,需要提供一个新类型作为标记(在我们的示例中为TypeSerializable::tag),提供了一种关联{ {1}}使用新标记(通过使用示例中的嵌套typedef,或通过特化trait类:T),最后重载实现函数(示例中为template <> talky_buffer_trait<T> { typedef new_tag type };

相关问题