调用模板函数而不是基类型的函数

时间:2014-12-22 13:38:37

标签: c++ templates overloading template-specialization c++03

我有一个类层次结构,可以使用operator<<写入对象。该示例如下所示:

#include <iostream>

struct Base
{
};

struct Derived : public Base
{
};

struct Shift
{
    template< typename U >
    Shift& operator<<(const U&)
    {
        std::cerr << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }

    Shift& operator<<(const Base&)
    {
        std::cerr << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }

#if 0
    Shift& operator<<(const Derived&)
    {
        std::cerr << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }
#endif
};

int main()
{
    Shift sh;
    Base bas;
    Derived der;
    int u32 = 0;

    sh << bas;
    sh << der;
    sh << u32;
}

这会产生以下输出:

Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const U&) [with U = Derived]
Shift& Shift::operator<<(const U&) [with U = int]

如果我取消注释#if 0部分,它将更改为所需的输出:

Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const Derived&)
Shift& Shift::operator<<(const U&) [with U = int]

我有很多派生类(实际上是整个层次结构),到现在为止,我必须为所有这些类型单独定义operator<<。我想要的是有一个解决方案,其中为从Base派生的所有类型调用基类型的运算符。这可能吗?

P.S。:我尝试了几种解决方案,例如写一个帮助类:

struct Base
{
};

struct Derived : public Base
{
};

template< typename T >
struct Helper
{
    static void shift()
    {
        std::cerr << __PRETTY_FUNCTION__ << std::endl;
    }
};

template< >
struct Helper< Base >
{
    static void shift()
    {
        std::cerr << __PRETTY_FUNCTION__ << std::endl;
    }
};

struct Shift
{
    template< typename U >
    Shift& operator<<(const U& value)
    {
        Helper< U >::shift();
        return *this;
    }
};

输出:

static void Helper<Base>::shift()
static void Helper<T>::shift() [with T = Derived]
static void Helper<T>::shift() [with T = int]

但仍调用基本模板,而不是Base专门化。

P.P.S。:不幸的是,我目前仅限C++03而没有Boost

1 个答案:

答案 0 :(得分:5)

在您当前的实现中,根据§13.3.3.1.4[over.ics.ref] / p1:

,首选模板化重载
  

当引用类型的参数直接(8.5.3)绑定到参数表达式时,隐式转换序列是标识转换,除非参数表达式的类型是参数类型的派生类,在这种情况下隐式转换序列是派生到基础的转换(13.3.3.1)。

由于派生到基础转换具有转换排名,因此通过身份转换实现所需的输出模板化版本通过使用具有适当条件的enable_if(SFINAE),必须在重载决策期间将秩排除在可行功能集之外:

#include <type_traits>

//...
template <typename U>
auto operator<<(const U&)
    -> typename std::enable_if<!std::is_base_of<Base, U>{}, Shift&>::type
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return *this;
}

Shift& operator<<(const Base&)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return *this;
}

输出:

Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const Base&)
typename std::enable_if<(! std::is_base_of<Base, U>{}), Shift&>::type Shift::operator<<(const U&) [with U = int; typename std::enable_if<(! std::is_base_of<Base, U>{}), Shift&>::type = Shift&]

中,实施的可读性较差:

template <bool B, typename T = void>
struct enable_if { typedef T type; };
template <typename T>
struct enable_if<false, T> {};
template <typename Base, typename Derived>
struct is_base_of
{
    typedef char (&yes)[1];
    typedef char (&no)[2];
    static yes test(Base*);
    static no test(...);
    static const bool value = sizeof(test((Derived*)0)) == sizeof(yes);
};
template <typename Base, typename Derived>
const bool is_base_of<Base, Derived>::value;

//...
template <typename U>
typename enable_if<!is_base_of<Base, U>::value, Shift&>::type operator<<(const U&)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return *this;
}

Shift& operator<<(const Base&)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return *this;
}