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

dav*_*idA 2 c++ namespaces boost-program-options deserialization c++-chrono

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

我使用C ++ 14不具有std::chrono::parse()(C ++ 20只),但我需要类型的deserialise值std::chrono::milliseconds(的特殊化std::chrono::duration从)istream

虽然这有效,但我找不到任何允许它不是 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;
}
}
Run Code Online (Sandbox Code Playgroud)

由于这两种参数类型都不是我的类型,我不确定如何在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);
Run Code Online (Sandbox Code Playgroud)

根据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),
             ^
Run Code Online (Sandbox Code Playgroud)

R S*_*ahu 5

即使标准允许运算符重载,我也不建议添加该函数。这不是std::chrono::milliseconds从 a读取 a 的通用方法std::istream。您拥有的是您的应用程序自己的从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;
   }
}
Run Code Online (Sandbox Code Playgroud)