如何为缺少预定义运算符而不扩展命名空间"std"的标准类型定义运算符>> (istream &, ...)?



显然,向std命名空间添加(几乎(任何内容都是未定义的行为。

我使用的是没有std::chrono::parse()的C++14(仅C++20(,但我需要从istream中取消std::chrono::milliseconds类型(std::chrono::duration的专业化(的值。

虽然这是有效的,但我找不到任何允许它不是UB的例外:

namespace std {
std::istream& operator >>(std::istream & is, std::chrono::milliseconds & ms) {
std::string s;
is >> s;
ms = std::chrono::milliseconds(std::stoi(s));
return is;
}
}

由于这两种参数类型都不是my类型,我不确定如何在std命名空间之外安全地定义此运算符。

请注意,该运算符将在Boost::program_options中被调用,因此我不认为我可以在自己的命名空间中定义该运算符,然后使用using my_ns::operator>>,因为using声明的作用域不会扩展到program_options作用域。

我是如何来到这里的

作为Boost::program_options的用户,我有一个特定的配置变量,我从存储为std::chrono::milliseconds值的配置文件中读取:

std::chrono::milliseconds period;
po::options_description config_only_opts;
config_only_opts.add_options()
("control.period", po::value<std::chrono::milliseconds>(&period), "Specify the period in milliseconds");
// ...
auto istream = ifstream("config.cfg");
po::variables_map vm;
po::store(po::parse_config_file(istream, config_file_opts, false), vm);

根据Boost::program_options文档,对于定义了operator >> (istream &, ...)函数的类型,可以从配置文件中取消序列化值。

如果不扩展到上面提到的std,我最终会出现这种编译器错误:

/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp: In instantiation of ‘struct boost::detail::deduce_target_char_impl<boost::detail::deduce_character_type_later<std::chrono::duration<long int, std::ratio<1, 1000> > > >’:
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:270:89:   required from ‘struct boost::detail::deduce_target_char<std::chrono::duration<long int, std::ratio<1, 1000> > >’
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:407:92:   required from ‘struct boost::detail::lexical_cast_stream_traits<std::__cxx11::basic_string<char>, std::chrono::duration<long int, std::ratio<1, 1000> > >’
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:468:15:   required from ‘struct boost::detail::lexical_converter_impl<std::chrono::duration<long int, std::ratio<1, 1000> >, std::__cxx11::basic_string<char> >’
/home/david/opt/boost-1.69/include/boost/lexical_cast/try_lexical_convert.hpp:201:44:   required from ‘bool boost::conversion::detail::try_lexical_convert(const Source&, Target&) [with Target = std::chrono::duration<long int, std::ratio<1, 1000> >; Source = std::__cxx11::basic_string<char>]’
/home/david/opt/boost-1.69/include/boost/lexical_cast.hpp:41:60:   required from ‘Target boost::lexical_cast(const Source&) [with Target = std::chrono::duration<long int, std::ratio<1, 1000> >; Source = std::__cxx11::basic_string<char>]’
/home/david/opt/boost-1.69/include/boost/program_options/detail/value_semantic.hpp:92:36:   required from ‘void boost::program_options::validate(boost::any&, const std::vector<std::__cxx11::basic_string<charT> >&, T*, long int) [with T = std::chrono::duration<long int, std::ratio<1, 1000> >; charT = char]’
/home/david/opt/boost-1.69/include/boost/program_options/detail/value_semantic.hpp:184:21:   required from ‘void boost::program_options::typed_value<T, charT>::xparse(boost::any&, const std::vector<std::__cxx11::basic_string<charT> >&) const [with T = std::chrono::duration<long int, std::ratio<1, 1000> >; charT = char]’
/home/david/myproj/Config.cpp:208:1:   required from here
/home/david/opt/boost-1.69/include/boost/lexical_cast/detail/converter_lexical.hpp:243:13: error: static assertion failed: Target type is neither std::istream`able nor std::wistream`able
BOOST_STATIC_ASSERT_MSG((result_t::value || boost::has_right_shift<std::basic_istream<wchar_t>, T >::value),
^

即使标准允许运算符重载,我也不建议添加该函数。这不是从std::istream读取std::chrono::milliseconds的通用方法。您所拥有的是应用程序自己从std::istream中读取这样一个对象的方式。

我建议在应用程序自己的namespace中添加一个函数。

namespace MyApp
{
std::istream& readChronoMilliSeconds(std::istream& is, std::chrono::milliseconds & ms)
{
std::string s;
is >> s;
ms = std::chrono::milliseconds(std::stoi(s));
return is;
}
}

为输入参数使用花哨的类型是在自欺欺人,因为输入参数通常只不过是数字、字符串或它们的列表。

我建议取一个合适的整数值,并在内部转换为毫秒。

int period_in_ms = 0;
...
config_only_opts.add_options()
("control.period_in_ms", po::value<int>(&period_in_ms), "Specify the period in milliseconds");
...
auto period = std::chrono::milliseconds(period_in_ms);

您可以使用Boost.ProgramOptions中的"通知"机制来更新period的值,但这不值得麻烦。

如果您想以某种格式输入参数(例如,以"ms"结束,如"100ms"(,则使用value<std::string>并进行转换/解析。

最新更新