自动模板类型扣除额令人困惑的指针和参考

Automatic template type deduction confusing pointers and references

本文关键字:指针 参考 类型 扣除额      更新时间:2023-10-16

在尝试调试一些代码时,我创建了一个类,以将对象的复杂层次结构倾倒到文本文件中,以便我可以比较一个情况,以便与它在情况下使用的情况。不是。我这样实施了这样的课程(缩小为裸露的例子):

#include <iostream>
class someOtherClass
{
public:
    someOtherClass()
        : a(0)
        , b(1.0f)
        , c(2.0)
    {}
    int a;
    float b;
    double c;
};
class logger
{
public:
    // Specific case for handling a complex object
    logger& operator << ( const someOtherClass& rObject )
    {
        std::cout << rObject.a << std::endl;
        std::cout << rObject.b << std::endl;
        std::cout << rObject.c << std::endl;
        return *this;
    }
    // [other class specific implementations]
    // Template for handling pointers which might be null
    template< typename _T >
    logger& operator << ( const _T* pBar )
    {
        if ( pBar )
        {
            std::cout << "Pointer handled:" << std::endl;
            return *this << *pBar;
        }
        else
            std::cout << "null" << std::endl;
        return  *this;
    }
    // Template for handling simple types.
    template< typename _T >
    logger& operator << ( const _T& rBar )
    {
        std::cout << "Reference: " << rBar << std::endl;
        return *this;
    }
};
int main(int argc, char* argv[])
{
    logger l;
    someOtherClass soc;
    someOtherClass* pSoc = &soc;
    l << soc;
    l << pSoc;
    pSoc = nullptr;
    l << pSoc;
    return 0;
}

我期望获得以下输出:

0
1
2
Pointer handled:
0
1
2
null

但是我真正得到的是:

0
1
2
Reference: 010AF7E4
Reference: 00000000

自动类型扣除似乎是在选择参考实现并将类型设置为someOtherClass*,而不是选择指针实现。我正在使用Visual Studio 2012。

logger& operator << ( const _T& rBar )类型T中可以是指针类型,因此为了正常工作,此模板需要一些限制:

template< typename _T , typename = typename ::std::enable_if_t<!std::is_pointer<_T>::value> >
logger& operator << ( const _T& rBar )
{
    std::cout << "Reference: " << rBar << std::endl;
    return *this;
}

在线编译器

这是必需的,因为当模板进行实例化时,将对_T = someOtherClass *变体进行const _T & pBar作为在这种情况下需要的转换序列,仅包括一个参考结合,该参考结合被认为是身份转换,而使用_T = someOtherClassconst _T* pBar变体将涉及拷贝iNitialization。

这里有一些修改和注释,随着该伐木类的增长并变得更加成熟,可能会有所帮助。

我试图:

a)解决不正确类型扣除的初始问题。

b)将记录仪从要记录的内容中解脱出来(否则您的记录器必须了解整个应用程序和所有库)。

c)提供了一种机制,即使是由第三方库提供的,即使提供任何类型的记录。

#include <iostream>
// I've put the logger and its helpers into a namespace. This will keep code tidy and help with
// ADL.
namespace logging 
{
    // define a general function which writes a value to a stream in "log format".
    // you can specialise this for specific types in std:: if you wish here
    template<class T> 
    void to_log(std::ostream& os, T const& value)
    {
        os << value;
    }
    // define a general function objects for writing a log-representation of tyoe T.
    // There are 2 ways to customise this.
    // a) provide a free function called to_log in the same namespace as your classes (preferred)
    // b) specialise this class.
    template<class T>
    struct log_operation
    {
        void operator()(std::ostream& os, T const& value) const
        {
            to_log(os, value);
        }
    };
    // specialise for any pointer
    template<class T>
    struct log_operation<T*>
    {
        void operator()(std::ostream& os, T* ptr) const
        {
            if (!ptr)
                os << "null";
            else
            {
                os << "->";
                auto op = log_operation<std::decay_t<T>>();
                op(os, *ptr);
            }
        }
    };
    // the logger is now written in terms of log_operation()
    // it knows nothing of your application's types
    class logger
    {
    public:
        // Template for handling any type.
        // not that this will also catch pointers.
        // we will disambiguate in the log_operation
        template< typename T >
        logger& operator << ( const T& rBar )
        {
            auto op = log_operation<std::decay_t<T>>();
            op(std::cout, rBar);
            std::cout << std::endl;
            return *this;
        }
    };
}
class someOtherClass
{
public:
    someOtherClass()
        : a(0)
        , b(1.0f)
        , c(2.0)
    {}
    int a;
    float b;
    double c;
};
// someOtherClass's maintainer provides a to_log function
void to_log(std::ostream& os, someOtherClass const& c)
{
    os << "someOtherClass { " << c.a << ", " << c.b << ", " << c.c << " }";
}
namespace third_party
{
    // the is in a 3rd party library. There is no to_log function and we can't write one which will be found with
    // ADL...
    struct classWhichKnowsNothingOfLogs {};
}
/// ..so we'll specialise in the logging namespace
namespace logging
{
    template<>
    struct log_operation<::third_party::classWhichKnowsNothingOfLogs>
    {
        void operator()(std::ostream& os, ::third_party::classWhichKnowsNothingOfLogs const& value) const
        {
            os << "classWhichKnowsNothingOfLogs {}";
        }
    };
}

int main(int argc, char* argv[])
{
    logging::logger l;
    someOtherClass soc;
    someOtherClass* pSoc = &soc;
    l << soc;
    l << pSoc;
    pSoc = nullptr;
    l << pSoc;
    l << third_party::classWhichKnowsNothingOfLogs();
    return 0;
}

预期输出:

someOtherClass { 0, 1, 2 }
->someOtherClass { 0, 1, 2 }
null
classWhichKnowsNothingOfLogs {}