简体   繁体   English

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

[英]How do I define operator >> (istream &, …) for a standard type that lacks a pre-defined operator without extending namespace `std`?

Apparently it's Undefined Behaviour to add ( almost ) anything to the std namespace.显然,将(几乎)任何东西添加到std命名空间是未定义的行为。

I'm using C++14 which has no std::chrono::parse() (C++20 only) but I need to deserialise values of type std::chrono::milliseconds (a specialisation of std::chrono::duration ) from an istream .我正在使用 C++14 ,它没有std::chrono::parse() (仅限 C++20),但我需要反序列化std::chrono::milliseconds类型的值( std::chrono::duration的特化) 来自istream

Although this works, I can't find any exceptions that allow this to not be UB:虽然这可行,但我找不到任何允许它不是 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;
}
}

Since neither of these two parameter types are my types, I'm not sure how I'd safely define this operator outside of the std namespace.由于这两种参数类型都不是我的类型,我不确定如何在std命名空间之外安全地定义此运算符。

Note that the operator will be called deep within Boost::program_options so I don't think I can define the operator in my own namespace and then use using my_ns::operator>> because the scope of that using declaration won't extend into the program_options scope.请注意,运算符将在Boost::program_options的深处调用,因此我认为我不能在自己的命名空间中定义运算符然后使用using my_ns::operator>>因为该using声明的 scope 不会扩展到程序选项program_options

How I Ended Up Here我是怎么来到这里的

As a user of Boost::program_options I have a particular configuration variable that I read from a configuration file that is stored as a std::chrono::milliseconds value:作为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);

According to the Boost::program_options documentation, it is possible to deserialise values from the config file for types that have an operator >> (istream &, ...) function defined.根据Boost::program_options文档,对于定义了operator >> (istream &, ...) function 的类型,可以从配置文件中反序列化值。

Without the extention to std mentioned above, I end up with this kind of compiler error:如果没有上面提到的对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),
             ^

I would not recommend adding that function even if the standard allowed the operator to be overloaded.即使标准允许运算符重载,我也不建议添加 function。 It's not a generic way to read a std::chrono::milliseconds from a std::istream .这不是从std::istream读取std::chrono::milliseconds的通用方法。 What you have is your application's own way of reading such an object from a std::istream .您所拥有的是您的应用程序自己从std::istream读取此类 object 的方式。

I recommend adding a function in your application's own namespace .我建议在应用程序自己的namespace中添加 function 。

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;
   }
}

Using fancy types for input parameter is fooling yourself because input parameters generally are nothing else than numbers, strings or lists of them.对输入参数使用花哨的类型是在自欺欺人,因为输入参数通常只是数字、字符串或它们的列表。

What I recommend is to take a proper integer value and convert internally to milliseconds.我建议采用适当的 integer 值并在内部转换为毫秒。

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);

You can use the "notify" mechanism in Boost.ProgramOptions to update the value of period in-place but it is not worth the trouble.您可以使用 Boost.ProgramOptions 中的“通知”机制来就地更新period的值,但这不值得麻烦。

If you want to input the parameter in a certain format (eg finish with "ms", as in "100ms") then take a value<std::string> and make the conversion/parsing.如果您想以某种格式输入参数(例如以“ms”结尾,如“100ms”),则取一个value<std::string>并进行转换/解析。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 std :: istream的运算符类型无效? - Invalid operator type of std::istream? 如何编写 `std::istream` 运算符 - How to write an `std::istream` operator 如何从std :: istream中读取(使用operator &gt;&gt;)? - How can I read from an std::istream (using operator>>)? 使用std :: istream :: operator &gt;&gt;处理无符号类型时,如何区分从下溢中提取失败? - With std::istream::operator>> working on unsigned types, how do I tell apart a failed extraction from an underflow? 在全局命名空间中为不依赖于用户定义类型的标准定义类型重载操作符是否格式正确? - Is it well-formed to overload an operator for a standard-defined type in global namespace that doesn't depend on a user-define type? 如何实现运算符&gt;&gt;(std::istream&amp;, std::array<char, N> &amp;)? - How to implement operator>>(std::istream&, std::array<char, N>&)? 我需要定义`operator ==`来将我的类与标准容器一起使用吗? - Do I need to define `operator==` to use my class with standard containers? 没有名称空间标准的朋友重载运算符 - friend overloaded operator without namespace std 在std命名空间中operator &lt;&lt;是什么做的? - what does operator<< in std namespace do? 如何将运算符发送到 std::async? - How do I send an operator into std::async?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM