代码之家  ›  专栏  ›  技术社区  ›  Charles Blessing

自动模板类型推导混淆指针和引用

  •  0
  • Charles Blessing  · 技术社区  · 7 年前

    在调试一些代码时,我创建了一个类,将复杂的对象层次结构的值转储到文本文件中,这样我就可以比较它工作的情况和不工作的情况。我实现了这样的类(简化为一个简单的示例):

    #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。

    2 回复  |  直到 7 年前
        1
  •  0
  •   user7860670    7 年前

    在里面 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;
    }
    

    Online compiler

    const _T & pBar 具有 _T = someOtherClass * const _T* pBar _T = someOtherClass 将涉及副本初始化。

        2
  •  0
  •   Richard Hodges    7 年前

    下面是一些修改和注释,随着这个日志类的增长和变得更成熟,它们可能会有所帮助。

    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 {}