【问题标题】:Template function gets called instead of function of base type调用模板函数而不是基类型的函数
【发布时间】:2014-12-22 13:38:36
【问题描述】:

我有一个可以使用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&lt;&lt; 定义。我想要的是有一个解决方案,其中基类型的运算符对从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

【问题讨论】:

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


    【解决方案1】:

    根据 §13.3.3.1.4 [over.ics.ref]/p1:

    ,在您当前的实现中,首选模板化重载

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

    由于派生到基础的转换被赋予了一个转换等级,为了实现所需的输出,模板化版本具有身份转换在重载解决期间,必须通过使用具有适当条件 (SFINAE) 的 enable_if 将等级从可行函数集中排除:

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

    【讨论】:

    • 这听起来很棒!不幸的是,我忘了提到我受限于 C++03。我会更新我的问题......也许我现在可以说服我的同事使用 C++11! :)
    • @hochl 在C++03中也可以达到同样的效果
    • 谢了!伟大的!感谢您的回答...我必须承认我并没有完全理解所有内容,我现在必须深入研究;)无论如何,感谢您的帮助!
    • @hochl 理解这个的关键词是“SFINAE
    • @hochl 只是为了使它类似于来自&lt;type_traits&gt;的官方实现
    猜你喜欢
    • 2017-03-25
    • 2012-03-06
    • 2013-03-15
    • 1970-01-01
    • 2019-01-09
    • 2011-09-16
    • 1970-01-01
    • 2011-02-19
    • 2011-01-01
    相关资源
    最近更新 更多