微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

带有“fmt”库的枚举的通用自定义流插入运算符

如何解决带有“fmt”库的枚举的通用自定义流插入运算符

我怀疑这个问题的答案相当明显,但由于某种原因我无法回答。

我正在尝试让 fmt 库对枚举类型使用通用流插入运算符。 fmt 认将枚举格式化为整数,但如果可用(至少在某些情况下),也会对用户定义的类型使用重载的插入运算符。

为了实验,我开始时没有使用 fmt,而是这样:

#include <iostream>
#include <ostream>
#include <type_traits>

enum class Enum0 { a,b,c,};

namespace foo {

enum class Enum1 { a,};

}

template <typename T>
std::enable_if_t<std::is_enum_v<T>,std::ostream &>
operator<<(std::ostream & stream,T const value) {
    return stream << static_cast<std::underlying_type_t<T>>(value);
}

int main() {
    std::cout << Enum0::b << std::endl;
    std::cout << foo::Enum1::c << std::endl;
}

输出

1
2

这使用带有标准输出流的自定义操作符(操作符被注释掉,它无法编译,正如预期的那样)。在这种情况下,即使在 foo::Enum1 命名空间中未定义运算符,也为 foo 正确选择了运算符。

然后我做了同样的事情,但添加fmt

#include <iostream>
#include <ostream>
#include <type_traits>

#include "fmt/format.h"
#include "fmt/ostream.h"

enum class Enum0 { a,T const value) {
    return stream << "custom operator: "
        << static_cast<std::underlying_type_t<T>>(value);
}

int main() {
    std::cout << Enum0::b << std::endl;
    std::cout << foo::Enum1::c << std::endl;

    fmt::print("{}\n",Enum0::b);
    fmt::print("{}\n",foo::Enum1::c);
}

输出

custom operator: 1
custom operator: 2
custom operator: 1
2

fmt 为全局命名空间中的枚举选择自定义运算符,但不为命名空间 foo 中的枚举选择自定义运算符。

如果我将运算符放在与枚举相同的命名空间中:

#include <iostream>
#include <ostream>
#include <type_traits>

#include "fmt/format.h"
#include "fmt/ostream.h"

namespace foo {

enum class Enum1 { a,};

template <typename T>
std::enable_if_t<std::is_enum_v<T>,T const value) {
    return stream << "custom operator: "
        << static_cast<std::underlying_type_t<T>>(value);
}

}

int main() {
    std::cout << foo::Enum1::c << std::endl;

    fmt::print("{}\n",foo::Enum1::c);
}

我明白了:

custom operator: 2
custom operator: 2

显示 fmt 在这种情况下选择了重载。但这并没有多大用处,因为重载旨在作为通用解决方案,并且不同的枚举可能位于不同的命名空间中。

讨论了似乎是类似的问题 here。提供了两种解决方案。一种是将重载放在 std 命名空间中。这是有效的,但是 rules 周围的环境相当严格,而且这个特定用例是否有效对我来说并不是很明显。线程中的另一个解决方案是将运算符放在 fmt 包含之前。我也试过了:

#include <iostream>
#include <ostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_enum_v<T>,T const value) {
    return stream << "custom operator: "
        << static_cast<std::underlying_type_t<T>>(value);
}

#include "fmt/format.h"
#include "fmt/ostream.h"

enum class Enum0 { a,};

}

int main() {
    std::cout << Enum0::b << std::endl;
    std::cout << foo::Enum1::c << std::endl;

    fmt::print("{}\n",foo::Enum1::c);
}

虽然它似乎对线程的参与者有效,但对我来说,fmt 仍然没有选择重载。 (我认为该线程来自于将枚举作为整数的认格式添加到库中之前,因此此行为可能从那时起发生了变化。)

有没有办法(除了将实现放在 std 命名空间中)为 << 将使用的枚举类型实现单个通用 fmt 运算符,即使在运算符在全局命名空间中,而枚举在另一个命名空间中?我意识到这里可能有一个非常明显的答案与查找规则或类似规则有关,但是如果有解决方案,我会错过它,所以我想我会在这里问。 (我知道专门化 fmt::formatter 是枚举格式的另一种选择,但我对使用 << 的通用解决方案是否可行感兴趣。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。