代码之家  ›  专栏  ›  技术社区  ›  Jere.Jones

如何重载不接收或返回ostream的运算符

  •  5
  • Jere.Jones  · 技术社区  · 17 年前

    // thread one
    Logger() << "Some string" << std::ios::hex << 45;
    // thread two
    Logger() << L"Some wide string" << std::endl;
    

    当前,我的记录器标题如下所示:

    #pragma once;
    #include <ostream>    
    class Logger
    {
    public:
        Logger();
        ~Logger();
    
        std::ostream* out_stream;
    };
    
    template <typename T>
    Logger& operator<< (Logger& logger, T thing) {
        *logger.out_stream << thing;
        return logger;
    }
    

    关于本课程的一些注意事项:

    1. 跨平台兼容性不是问题。
    2. 记录器构造函数和解构器执行单例的必要锁定。

    我有三个问题:

    • 我如何使操作员<&书信电报;操纵器的功能工作?
    • 如何添加专门化,以便在将T传递到out_流之前,如果T是WCHAR*或std::wstring,它将转换为char*或std::string?(我可以进行转换。在我的情况下,丢失高unicode字符不是问题。)

    总结在回答中学到的东西:

    • 将模板放在朋友之前而不是之后。
    • std::ios::hex不是操纵器。hex是一个操纵器。

    最终结果

    #pragma once
    #include <ostream>
    #include <string>
    
    std::string ConvertWstringToString(std::wstring wstr);
    
    class Logger
    {
    public:
        Logger();
        ~Logger();
    
        template <typename T>
        Logger& operator<< (T data) {
            *out << data;
            return *this;
        }
        Logger& operator<< (std::wstring data) {
            return *this << ConvertWstringToString(data);
        }
        Logger& operator<< (const wchar_t* data) {
            std::wstring str(data);
            return *this << str;
        }
    
    private:
        std::ostream* out;
    };
    
    4 回复  |  直到 17 年前
        1
  •  7
  •   Johannes Schaub - litb    17 年前

    您可以使用友元定义,它将在类的周围命名空间中定义运算符,并使其仅对运算符重载解析可见(不能使用::operator<…语法手动调用):

    class Logger
    {
    public:
        Logger();
        ~Logger();
    
        std::ostream* out_stream;
    
        template <typename T>
        friend Logger& operator<< (Logger& logger, T thing) {
            *logger.out_stream << thing;
            return logger;
        }
    
        /* special treatment for std::wstring. just overload the operator! No need
         * to specialize it. */
        friend Logger& operator<< (Logger& logger, const std::wstring & wstr) {
            /* do something here */
        }
    
    };
    

    另一种方法是,保持代码不变,只需让操作员<&书信电报;如果您是一个朋友,请将这一行添加到您的类定义中:

    template <typename T>
    friend Logger& operator<< (Logger& logger, T thing);
    

    #include <iostream>
    #include <cstdlib>
    using namespace std;
    
    template<typename Char, typename Traits = char_traits<Char> >
    struct logger{
        typedef std::basic_ostream<Char, Traits> ostream_type;
        typedef ostream_type& (*manip_type)(ostream_type&);
        logger(ostream_type& os):os(os){}
        logger &operator<<(manip_type pfn) {
            if(pfn == static_cast<manip_type>(std::endl)) {
                time_t t = time(0);
                os << " --- " << ctime(&t) << pfn; 
            } else
                os << pfn;
            return *this; 
        }
        template<typename T> 
        logger &operator<<(T const& t) { 
            os << t; 
            return *this;
        }
    private:        
        ostream_type & os;
    };
    
    namespace { logger<char> clogged(cout); }
    int main() { clogged << "something with log functionality" << std::endl; }
    

    };

    请注意,它是std::hex,但不是std::ios::hex . 后者用作的操纵器标志 setf

        2
  •  2
  •   Adam Rosenfield    17 年前

    使用模板是正确的方法,但您只需确保模板位于 标题 logger.h ,或者你叫什么来着), 在实现文件中( logger.cpp )。这将自动适用于具有 operator << 定义为 std::ostream std::ostream 只调用 ostream .

    你可以 操作员<&书信电报;

    template <typename T> friend Logger& operator<< (Logger& logger, T thing);
    

    template <typename T>
    Logger& operator<< (Logger& logger, T thing) {
        *logger.out_stream << thing;
        return logger;
    }
    
    // Template specialization - the "template <>" part is necessary
    template <>
    Logger& operator<< (Logger& logger, const wchar_t *wstr)
    {
      // convert wstr to an ANSI string and log it
    }
    
    template <>
    Logger& operator<< (Logger& logger, const std::wstring & wstr)
    {
      // convert wstr to an ANSI string and log it
    }
    
        3
  •  2
  •   James Curran    17 年前

    无需友谊宣言:

    class Logger
    {
    public:
        Logger();
        ~Logger();
    
    template <typename T>
    inline Logger& Display(T thing)
    {
       *out_stream << thing;
        return *this;
    }
    private:
        std::ostream* out_stream;
    };
    
    template <typename T>
    Logger& operator<< (Logger& logger, T thing) 
    {
        return logger.Display(thing);
    }
    
        4
  •  0
  •   Lodle    17 年前

    为什么不使用printf方法并使用多参数方法(使用三个点…)。这仍然为您提供了很多格式化功能,并且不会像使用<&书信电报;。

    Logger("This is my log msg %0X", 45);
    

    请稍等两秒钟,我会为您调出一个代码示例。

    编辑:

    void Logger(const char* format, ...)
    {
        char szMsg[3000];
    
        va_list args;
        va_start( args, format );
        vsnprintf( szMsg, sizeof(szMsg) - 1, format, args );
        va_end(args);
    
        // code to print szMsg to a file or whatever here
    }
    

    如果希望将其用作类而不是独立函数,则可以重载logger操作符(),它的工作方式也一样