枚举类的运算符<<不起作用

operator<< for enum class just doesn't work

提问人:TheEagle 提问时间:8/29/2023 更新时间:8/29/2023 访问量:71

问:

我有以下代码:

日志记录.hxx

#include <iostream>
#include <string>
#include <sstream>

#include "singleton.hxx"
#include "utils.hxx"

class FGaugeLogger: public Singleton<FGaugeLogger> {
    public:
        enum class LogLevel: int {
            DEBUG = 0,
            INFO = 1,
            WARNING = 2,
            ERROR = 3,
            FATAL = 4,
        };
        
        FGaugeLogger() {};
        
        template<typename T>
        void log(LogLevel level, const T& msg) {
            if (level >= _level) {
                if (level >= LogLevel::WARNING) {
                    std::cerr << "[" << level << "] " << msg << std::endl;
                } else {
                    std::cout << "[" << level << "] " << msg << std::endl;
                }
            }
        }
    
    private:
        LogLevel _level {LogLevel::DEBUG};
};

std::ostream& operator<<(std::ostream& s, const FGaugeLogger::LogLevel level);

#define LOG(level, expr) \
    std::stringstream ss; \
    ss << expr; \
    std::string str = ss.str(); \
    FGaugeLogger::instance()->log(FGaugeLogger::LogLevel::level, ss.str());

logging.cxx

#include "logging.hxx"

std::ostream& operator<<(std::ostream& s, FGaugeLogger::LogLevel level) {
    s << "level";
    return s;
}

main.cxx

#include "logging.hxx"

int main(int argc, char* argv[]) {
    LOG("info message");
    return 0;
}

这会在编译时给出以下消息,然后是 100 个不匹配的重载:

In file included from /media/frederic/WD-5TB/.fg/Programme/fgauge/src/main.cxx:1:
/media/frederic/WD-5TB/.fg/Programme/fgauge/src/logging.hxx: In member function ‘void FGaugeLogger::log(FGaugeLogger::LogLevel, const T&)’:
/media/frederic/WD-5TB/.fg/Programme/fgauge/src/logging.hxx:27:58: error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream<char>’ and ‘FGaugeLogger::LogLevel’)
   27 |                                         std::cerr << "[" << level << "] " << msg << std::endl;
      |                                         ~~~~~~~~~~~~~~~~ ^~ ~~~~~
      |                                                   |         |
      |                                                   |         FGaugeLogger::LogLevel
      |                                                   std::basic_ostream<char>

任何想法这里可能有什么问题吗?AFAICS 我的代码与这个类似问题的答案中的第三个示例相同。

C++ 重载运算符 枚举类

评论

1赞 273K 8/29/2023
您能否发布一个最小的可重现示例,而不依赖于未知类?例如,是不必要的。public Singleton<FGaugeLogger>
0赞 Jarod42 8/29/2023
移动定义以下声明template<typename T> void FGaugeLogger::log(LogLevel level, const T& msg)std::ostream& operator<<(std::ostream& s, const FGaugeLogger::LogLevel level);
0赞 Jarod42 8/29/2023
[OT]:对 MACRO 参数使用 parent,即避免运算符优先级问题。ss << (expr);

答:

3赞 Jan Schultke 8/29/2023 #1

问题是 C++ 文件是从上到下解析的,事情的顺序通常很重要。在你写的地方:

std::cerr << "[" << level << "] " << msg << std::endl;

for 尚未声明。operator<<FGaugeLogger::LogLevel

一个简单的解决方法是将其声明为类内部:friend

class FGaugeLogger: public Singleton<FGaugeLogger> {
  public:
    enum class LogLevel: int {
        DEBUG = 0,
        INFO = 1,
        WARNING = 2,
        ERROR = 3,
        FATAL = 4,
    };
    // note: parameter names are optional, and it's probably better to omit them
    friend std::ostream& operator<<(std::ostream&, FGaugeLogger::LogLevel);
    // ...
};

请参阅编译器资源管理器中的最小示例

您还可以在类中定义它,使其成为隐藏的友元(请参阅隐藏的友元:声明和定义)。

替代解决方案

您也可以在定义 之后定义 out-of-line,例如logoperator<<

template<typename T>
void FGaugeLogger::log(LogLevel level, const T& msg) { /* ... */ }

但是,与第一个解决方案相比,这总体上需要更多的工作,后者只需要您将代码移动到类中并使其 .外行定义成员函数模板需要一些代码重复。friend