使用Y组合器进行递归访问-应该编译吗?

时间:2018-10-11 20:36:51

标签: c++ compiler-errors variant visitor y-combinator

我一直在用优雅的(?)方式为std::variant编写访问者,我不确定我在做什么是否有效的C ++(17),因为在Clang期间GCC是否会按照我的预期进行给我一个错误。在下文中,我首先提供一些背景信息,以使问题更易于理解。如果您之前已经看过这种事情,则可能想跳到最后一个问题。


写访问者的一个巧妙窍门包括从处理不同类型变量的一堆lambda派生出来,并通过可变using重载其调用运算符:

template <typename... Base>
struct visitor : Base... {
    using Base::operator()...;
};

其中一个变得有些麻烦的地方是递归访问包含自身集合的变量类型。考虑例如此变体类型var可以是int或它本身的向量:

struct var_vec; // forward declaration
using var = std::variant<int, var_vec>;
struct var_vec : std::vector<var> {
    using std::vector<var>::vector;
};

一个人不能简单地在处理std::visit情况的lambda中再次调用var_vec,因为visitor对象在当时是不完整的类型:

visitor{
    [](int i) -> void { std::cout << i << std::endl; },
    [](var_vec const & x) -> void {
        for (var const & y : x)
            std::visit(/* ? */, y);
    }
};

不幸的是,对于这个难题,有一个非常漂亮的解决方案,其中涉及Y combinator

Y{[](auto const & self, auto const & x) -> void {
      visitor{
          [](int i) -> void { std::cout << i << std::endl; },
          [&self](var_vec const & x) -> void {
              for (var const & y : x)
                  std::visit(self, y);
          }
      }(x);
  }}

其中Y通过递归应用{{1},实际上将两个自变量(函子selfx)的仿函数变成仅一个自变量x的仿函数。 }。象征性地:

f

在C ++中(Yf)(x) = f(f(f(..., x), x), x) 的最小实现可能看起来像这样:

Y

新的元编程库template <typename F> struct Y { template <typename... X> auto operator()(X &&... x) const { return f(*this, std::forward<X>(x)...); } F f; }; 实际上提供了boost::hana的标头,它们称为overload,以及Y组合器的标头,它们称为fix。他们的实现在Clang中遇到的错误与我的完全相同。


到目前为止,这正是我要实现的目标的逻辑。基于上述,我想到了这个minimum working example on Compiler Explorer。它可以在GCC 7和8上完美地编译,但对于Clang却可以提供错误信息

  

错误:函数'visit:38:11)>&,推断出返回类型的const std :: variant&>'   在定义之前使用

以及

  

错误:没有匹配函数可调用'const(lambda)类型的对象   在:38:11)'

visitor

最后进入 return f(*this, std::forward<X>(x)...); ^

  

错误:没有匹配的函数调用'__invoke'

<variant>

  

错误:constexpr变量“ _S_vtable”必须由   常数表达式

    return std::__invoke(std::forward<_Visitor>(__visitor),
           ^~~~~~~~~~~~~

尤其是最后几个错误使我认为这可能是Clang错误? 有人能确认这确实是正确的C ++代码还是Clang在拒绝编译时是否正确?

0 个答案:

没有答案