2016-10-02 54 views
4

我想访问使用lambdas和过载创建函数(例如boost::hana::overload一个“递归”std::variant一个`的std :: variant`。递归来访使用lambdas和固定点组合


假设我有一个叫做my_variant变量类型,可以存储一个一个int,一个floatvector<my_variant>

struct my_variant_wrapper; 

using my_variant = 
    std::variant<int, float, std::vector<my_variant_wrapper>>; 

struct my_variant_wrapper 
{ 
    my_variant _v; 
}; 

(我为了定义使用包装my_variant_wrapper类)


我想递归访问变体打印不同的东西取决于存储的类型。这里有一个working example使用struct基于访问者:

struct struct_visitor 
{ 
    void operator()(int x) const { std::cout << x << "i\n"; } 
    void operator()(float x) const { std::cout << x << "f\n"; } 

    void operator()(const std::vector<my_variant_wrapper>& x) const 
    { 
     for(const auto& y : x) std::visit(*this, y._v); 
    } 
}; 

上述访客呼叫std::visit打印所需的输出:

my_variant v{ 
    std::vector<my_variant_wrapper>{ 
     my_variant_wrapper{45}, 
     std::vector<my_variant_wrapper>{ 
      my_variant_wrapper{1}, my_variant_wrapper{2} 
     }, 
     my_variant_wrapper{33.f} 
    } 
}; 

std::visit(struct_visitor{}, v); 

// Prints: 
/* 
    45i 
    1i 
    2i 
    33f 
*/ 

我想在本地创建访问者为一系列的使用boost::hana::overloadboost::hana::fix重载lambda。

fixY-combinator的实现,它可以用于实现类型推导lambda表达式中的递归。 (有关详细信息,请参阅this question。)

这是我试过了,和预期的工作:

namespace bh = boost::hana; 
auto lambda_visitor = bh::fix([](auto self, const auto& x) 
    { 
     bh::overload(
      [](int y){ std::cout << y << "i\n"; }, 
      [](float y){ std::cout << y << "f\n"; }, 
      [&self](const std::vector<my_variant_wrapper>& y) 
      { 
       for(const auto& z : y) std::visit(self, z._v); 
      })(x); 
    }); 

我的理由如下:

  • boost::hana::fix返回一元通用lambda可以用作std::variant的访问者。

  • boost::hana::fix需要一个二元通用lambda,其中第一个参数是允许递归lambda的一元函数,第二个参数是lambda体的初始参数。

  • 调用boost::hana::overload与处理所有可能的类型里面my_variant产生某种访问者这相当于struct_visitor

  • 使用self而不是lambda_visitorconst std::vector<my_variant_wrapper>&里面应该允许递归正常工作。

  • 立即调用创建的超载与bh::overload(...)(x)应触发递归访问。

不幸的是,as you can see in this wandbox example,该lambda_visitor例如编译失败,喷涌出大量的几乎不可破译的模板重的错误:

...

/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50: error: use of 'main():: [with auto:2 = boost::hana::fix_t >; auto:3 = int]' before deduction of 'auto' { return f(fix(f), static_cast(x)...); }

...


的错误似乎相似,我会得到不使用boost::hana::fix

auto lambda_visitor = bh::overload(
    [](int y){ std::cout << y << "i\n"; }, 
    [](float y){ std::cout << y << "f\n"; }, 
    [](const std::vector<my_variant_wrapper>& y) 
    { 
     for(const auto& z : y) std::visit(lambda_visitor, z._v); 
    }); 

std::visit(lambda_visitor, v); 

error: use of 'lambda_visitor' before deduction of 'auto' for(const auto& z : y) std::visit(lambda_visitor, z._v);


我在做什么错?是否有可能通过使用fix,overload和一组lambda来实现本地递归变体访问

我的直觉是,lambda_visitor本来“相当于”struct_visitor,这要归功于fix提供的间接。

+0

GCC HEAD [编译失败甚至升压::花:: fix'的'最基本的例子(http://melpon.org/ wandbox/permlink/P8B4X36hxbpv2FwS)(GCC 6.1和Clang HEAD工作正常),所以我认为这里的问题与'std :: variant'完全无关。使用libstdC++ HEAD尝试Clang HEAD会很有趣,但我不知道如何在wandbox上做到这一点,即使所有位都明显存在...... – ildjarn

+0

您可以在“编译器选项”中设置'-stdlib = libstdC++' “ 部分。它似乎与叮当妥善编译后,摆脱iostreams。 –

+1

“*您可以在”编译器选项“部分设置'-stdlib = libstdC++'*”您可以[但它似乎没有实际执行任何操作](http://melpon.org/wandbox/permlink/ yEQ1kdCEv8dgbbc3)...; - ] – ildjarn

回答

6

让我们选一个更简单的例子。我们想要使用定点组合器来实现gcd。第一次去可能是这样的:

auto gcd = bh::fix([](auto self, int a, int b) { 
    return b == 0 ? a : self(b, a%b); 
}); 

std::cout << gcd(12, 18); 

这种失败与海湾合作委员会最终产生这个错误编译:

/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50: error: use of 'main()::<lambda(auto:2, int, int)> [with auto:2 = boost::hana::fix_t<main()::<lambda(auto:2, int, int)> >]' before deduction of 'auto' 
     { return f(fix(f), static_cast<X&&>(x)...); } 
               ^

我们传递至fix()拉姆达有一个推断返回类型。但我们如何推断它呢?只有一个返回语句,而且是递归的!我们需要给编译器一些帮助。要么我们需要掰开我们return语句,这样,一个有一个明确的类型:

auto gcd = bh::fix([](auto self, int a, int b) { 
    if (b == 0) { 
     return a; 
    } 
    else { 
     return self(b, a%b); 
    } 
}); 

或者干脆提供返回类型明确:

auto gcd = bh::fix([](auto self, int a, int b) -> int { 
    return b == 0 ? a : self(b, a%b); 
}); 

这些选项编译和工作。

您的原始示例也是如此。如果你只是指定的拉姆达返回void,一切正常:

auto lambda_visitor = bh::fix([](auto self, const auto& x) -> void 
//              ^^^^^^^^ 
    { 
     bh::overload(
      [](int y){ std::cout << y << "i\n"; }, 
      [](float y){ std::cout << y << "f\n"; }, 
      [&self](const std::vector<my_variant_wrapper>& y) 
      { 
       for(const auto& z : y) std::visit(self, z._v); 
      })(x); 
    }); 

std::visit(lambda_visitor, v); 
+1

那么有什么问题[这里](http://melpon.org/wandbox/permlink/P8B4X36hxbpv2FwS)呢? – ildjarn

+1

@ildjarn可能是gcc回归,我提交了一个bug报告。使用[this y combinator](http:// stackoverflow。com/documentation/c%2b%2b/572/lambda/8508/recursive-lambdas)为了避免hana依赖,我可以在gcc 4.9,5.3和6.1上编译因子,但不是7.0。 – Barry