【问题标题】:Why is noexcept a problem for boost spirit semantic action in c++17?为什么 noexcept 是 c++17 中提升精神语义动作的问题?
【发布时间】:2021-11-21 06:17:24
【问题描述】:

以下代码在 VS2019 上使用 c++17 模式给了我奇怪的编译器错误:

#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

class Duration
{
  std::chrono::duration<int64_t, std::nano> d;

public:
  void addHours(uint64_t val) noexcept
  {
  }
};


int main()
{
  Duration d;
  namespace spirit = boost::spirit;
  namespace phoenix = boost::phoenix;
  namespace qi = boost::spirit::qi;
  qi::int_parser<int64_t, 10> int64_;
  std::string s = "2h";
  boost::spirit::qi::rule<std::string::iterator, Duration()> durationRule = (int64_ >> "h")[phoenix::bind(&Duration::addHours, qi::_val, qi::_1)];

  boost::spirit::qi::phrase_parse(s.begin(), s.end(), durationRule, boost::spirit::ascii::space, d);
}

如果我用 c++14 模式编译它一切都很好,但是用 c++17 或 c++20 编译它会失败。我发现问题出在 addHours 方法后面的 noexcept 关键字上。谁能解释为什么这是个问题?

【问题讨论】:

  • "...下面的代码给了我奇怪的编译器错误..."愿意分享吗?

标签: c++ visual-studio boost c++17 boost-spirit-qi


【解决方案1】:

Phoenix 库早于 noexcept。

c++17 起The noexcept-specification is a part of the function type and may appear as part of any function declarator.

言下之意就是在c++17模式下使用noexcept规范会遇到Phoenix的限制。

具体来说,似乎破坏的是返回类型推导:仅在异常规范上不同的函数不能被重载(就像返回类型一样,异常规范是函数类型的一部分,但不是函数的一部分签名)(C++17 起)。

改进

在您的情况下,既然您使用的是 C++17,为什么不简化整个事情呢? Live On Coliru

qi::rule<std::string::const_iterator, Duration()> durationRule //
    = qi::eps[_val = 0s]                                       //
    >> (qi::int_ >> "h")[_val += _1 * 1h];

输出

"2m" -> failed
"3h" -> 10800
"4s" -> failed

泄露了我的意图!

额外奖励 1:单位

使用简单的表格

qi::symbols<char, Duration> unit_;
unit_.add
    ("ns", 1ns) ("nano", 1ns)
    ("us", 1us) ("μs", 1us) ("micro", 1us)
    ("ms", 1ms) ("milli", 1ms)
    ("s", 1s)
    ("m", 1min) ("min", 1min)
    ("h", 1h) ("hour", 1h)
    ("d", 24h) ("day", 24h)
    ;

现在有了细微的变化

    >> +(qi::int_ >> unit_)[_val += _1 * _2];

它确实评估了大量有趣的序列:Live On Coliru

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <chrono>
#include <iomanip>

using namespace std::chrono_literals;
using Duration = std::chrono::duration<int64_t, std::nano>;
namespace qi   = boost::spirit::qi;

int main()
{
    using namespace qi::labels; // _val, _1 etc

    qi::symbols<char, Duration> unit_;
    unit_.add
        ("ns", 1ns) ("nano", 1ns)
        ("us", 1us) ("μs", 1us) ("micro", 1us)
        ("ms", 1ms) ("milli", 1ms)
        ("s", 1s)
        ("m", 1min) ("min", 1min)
        ("h", 1h) ("hour", 1h)
        ("d", 24h) ("day", 24h)
        ;

    qi::int_parser<int64_t, 10> int64_;
    qi::rule<std::string::const_iterator, Duration(), qi::blank_type>
        durationRule         //
        = qi::eps[_val = 0s] //
        >> +(int64_ >> unit_)[_val += _1 * _2];

    for (std::string const s :
         {
             "2m",
             "3h",
             "4s",
             "1ms -1ns",
             "1 day -23h",
             "3600000000ns",
             "1 day -23h -50m +3600000000ns",
         }) //
    {
        std::cout << std::quoted(s);

        Duration d{};
        if (phrase_parse(                //
                s.begin(), s.end(),      //
                durationRule >> qi::eoi, //
                qi::blank, d))
            std::cout << " -> " << d / 1.0s << "s\n";
        else
            std::cout << " -> failed\n";
    }
}

打印

"2m" -> 120s
"3h" -> 10800s
"4s" -> 4s
"1ms -1ns" -> 0.000999999s
"1 day -23h" -> 3600s
"3600000000ns" -> 3.6s
"1 day -23h -50m +3600000000ns" -> 603.6s

请注意,我已经改变了一些细节以使其正常工作。值得注意的是 qi::eoi 检查整​​个输入是否被消耗,并在规则中声明船长(blank 而不是 space 以排除换行符)。否则,无论如何,该规则都是隐含的词位 (Boost spirit skipper issues)

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
  • 1970-01-01
  • 2013-06-09
相关资源
最近更新 更多