xtensor存储std::variant时operator<<重载无法被识别的问题
xtensor中打印std::variant时自定义operator<<不生效的解决方法
希望在xtensor中存储std::variant类型,但打印xtensor对象时,自定义的operator<<重载并未被调用。单独打印std::variant变量正常,但打印xtensor对象时重载不生效,相关代码如下:
#include <iostream> #include <xtensor/xarray.hpp> #include <xtensor/xio.hpp> #include <xtensor/xview.hpp> #include <xtensor/xadapt.hpp> #include <variant> template<typename T, typename... Ts> std::ostream &operator<<(std::ostream &os, const std::variant<T, Ts...>& v) { std::visit([&os](auto &&arg) { os << arg; }, v); return os; } void foo() { xt::xtensor<std::variant<double, std::string>, 2>::shape_type shape = {1,1}; xt::xtensor<std::variant<double, std::string>, 2> ret(shape); std::variant<double, std::string> a = 2.5; ret.at(0, 0) =a; std::cout << a << std::endl; //works std::cout << ret << std::endl; //doesn`t work }
问题原因
xtensor的IO实现位于xt命名空间,当它输出容器内的std::variant元素时,会通过**依赖参数查找(ADL)**匹配operator<<。ADL仅会搜索:
- 函数参数类型所在的命名空间(
std,因为std::variant属于标准库命名空间) - 当前函数所在的命名空间(
xt)
你定义的operator<<在全局命名空间,不在ADL的搜索范围内,因此xtensor的代码无法找到该重载,导致输出失败。
解决方案
有两种可行的解决方式,推荐第二种更符合标准规范的方法:
方法1:将operator<<重载放入std命名空间
把自定义的operator<<移到std命名空间中,让ADL能够找到它:
#include <iostream> #include <xtensor/xarray.hpp> #include <xtensor/xio.hpp> #include <variant> namespace std { template<typename T, typename... Ts> std::ostream &operator<<(std::ostream &os, const std::variant<T, Ts...>& v) { std::visit([&os](auto &&arg) { os << arg; }, v); return os; } } void foo() { xt::xtensor<std::variant<double, std::string>, 2> ret({1,1}); std::variant<double, std::string> a = 2.5; ret.at(0, 0) = a; std::cout << a << std::endl; std::cout << ret << std::endl; // 现在正常输出 } int main() { foo(); return 0; }
注意:虽然C++标准不建议随意扩展
std命名空间,但针对标准库模板类型的重载是允许的,这是解决此类ADL问题的常用手段。
方法2:利用xtensor的自定义输出扩展点
通过特化xt::xformat_impl结构体,让xtensor的IO系统直接支持std::variant的输出,无需修改std命名空间:
#include <iostream> #include <xtensor/xarray.hpp> #include <xtensor/xio.hpp> #include <variant> namespace xt { // 特化xformat_impl以处理std::variant template<typename T, typename... Ts> struct xformat_impl<std::variant<T, Ts...>> { static void format(std::ostream& os, const std::variant<T, Ts...>& v, const xformat_config& config) { std::visit([&os, &config](const auto& arg) { // 复用xtensor内置的格式化逻辑处理variant中的元素 xformat_impl<std::decay_t<decltype(arg)>>::format(os, arg, config); }, v); } }; } // 保留全局的operator<<供单独打印variant使用 template<typename T, typename... Ts> std::ostream &operator<<(std::ostream &os, const std::variant<T, Ts...>& v) { std::visit([&os](auto &&arg) { os << arg; }, v); return os; } void foo() { xt::xtensor<std::variant<double, std::string>, 2> ret({1,1}); std::variant<double, std::string> a = 2.5; ret.at(0, 0) = a; std::cout << a << std::endl; std::cout << ret << std::endl; // 现在正常输出 } int main() { foo(); return 0; }
这种方法利用xtensor提供的扩展机制,更符合C++标准的设计规范,是推荐的解决方案。
内容的提问来源于stack exchange,提问作者MarcinG
相关产品推荐
相关产品推荐

