繁体   English   中英

C ++:使用C ++ 14通用Lambda增强融合折叠

[英]C++: boost fusion fold with c++14 generic lambdas

我试图将通用的lambda函数传递给boost :: fusion :: fold函数,以便我可以迭代boost :: fusion :: vector的所有元素。 我的目标是从向量中的每个元素调用一个非常量成员函数。 问题在于,即使向量包含非常量值,由通用lambda推导的类型也是const引用。 这导致我的gcc-4.9.0编译器(使用CygWin)抱怨我正在丢弃const限定符。

#include <iostream>                                        
#include <boost/fusion/include/vector.hpp>                             
#include <boost/fusion/include/fold.hpp>                               
#include <boost/fusion/include/for_each.hpp>                               

class Silly {                                              

public:                                                
  Silly(int x)                                             
    : x_(x){}                                              

  int increment(int i) {                                       
    return x_ += i;                                        
  }                                                                                                                                        

private:                                               
  int x_;                                              
};                                                 

using my_type = boost::fusion::vector<Silly, Silly>;                           


int main() {                                               
  my_type my_vector(1, 2);                                     
  boost::fusion::fold(my_vector, 0, [](int i, auto& x){return x.increment(i);}); //error: passing 'const Silly' as 'this' argument of 'int Silly::increment(int)' discards qualifiers                                      
}                                                                                              

现在,如果我通过以下函子而不是lambda,程序将干净地编译

struct functor {

  template <class X>
  int operator()(int i, X& x) {
  return x.increment(i);
 }
};

这是boost :: fusion错误还是我错过了什么? 提前致谢!

有多个boost::fusion::fold重载。 boost的svn repo中

template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
    Seq const
  , State const
  , F
>::type
BOOST_FUSION_FOLD_NAME(Seq const& seq, State const& state, F f)
{
    return result_of::BOOST_FUSION_FOLD_NAME<Seq const,State const,F>::call(
        state,
        seq,
        f);
}

template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
    Seq
  , State const
  , F
>::type
BOOST_FUSION_FOLD_NAME(Seq& seq, State& state, F f)
{
    return result_of::BOOST_FUSION_FOLD_NAME<Seq,State,F>::call(
        state,
        seq,
        f);
}

template<typename Seq, typename State, typename F>
inline typename result_of::BOOST_FUSION_FOLD_NAME<
    Seq const
  , State const
  , F
>::type
BOOST_FUSION_FOLD_NAME(Seq const& seq, State& state, F f)
{
    return result_of::BOOST_FUSION_FOLD_NAME<Seq const,State,F>::call(
        state,
        seq,
        f);
}

编译器允许实例化类模板result_of::BOOST_FUSION_FOLD_NAME在所有的返回类型,这些变体一旦类型推演和替换(*)已经成功,选择了一个载之前 在这种情况下,编译器必须实例化该类模板,以确定返回类型是否有效。 如果在返回类型中替换(替换模板参数)在立即上下文中导致无效类型,则将丢弃重载。 这称为SFINAE。

(*)此名称通常解析为result_of::fold

现在,具有Seq const&参数的重载之一的实例将尝试确定lambda的返回类型。 但是,使用Silly const&第二个参数实例化lambda失败:无法在const对象上调用increment (这是编译器告诉您的)。

如果确定返回类型失败,这将导致我们尝试确定其返回类型的fold重载替换失败。 但是,由于lambda和C ++ 14函数中的自动返回类型推导而导致的替换失败不在原始模板fold的直接上下文中:它们发生使用自动返回类型推导的函数中(此处为lambda)。

不在原始模板的直接上下文中发生的替换失败是一个硬错误,它不是可以从中恢复的SFINAE类型的错误。 (SFINAE = SFIICINAE)

如果您显式指定了lambda的返回类型, [](int i, auto& x) -> int {return x.increment(i);} ,则无需实例化函数/ lambda即可确定返回类型。 可以仅从声明中确定。 因此,对于任何重载,都不会发生基于返回类型的替换失败,并且通常的重载解决方案可以选择适当的重载。 选择非常量Seq&重载,并且lambda的实例化将有效。

同样,对于显式编写的函子:如果可以在不实例化函数的情况下确定返回类型,则不会发生错误。 如果将C ++ 14的返回类型推导用于普通函数,则会发生相同的问题:

struct functor {
  template <class X>
  auto operator()(int i, X& x) {
  return x.increment(i);
 }
};

附带说明:如TC注释中所述,以下功能对象类型也会发生硬错误:

struct functor {
  int operator()(int i, Silly& x) {
  return x.increment(i);
 }
};

但是,失败的原因有所不同:同样,所有fold重载都需要使用各自的类型实例化result_of::fold类模板。 但是,此类模板不会在立即上下文中产生替换错误:如果无法通过传递的参数类型调用传递的函数,则将发生硬错误。

由于不能使用intSilly const&类型的参数调用int(int, Silly&)类型的Silly const& ,因此会发生硬错误。

当将apply运算符作为模板编写时(例如在C ++ 14返回类型推导的示例中),可以为Silly const&类型的第二个参数实例化operator()模板的声明X将推导为Silly const )。 但是,不能实例化函数定义 ,因为这将导致与OP中相同的错误: Silly::increment需要一个非常量Silly对象。

但是, 如果没有返回类型推导 ,则函数定义的实例化仅在重载解析之后发生。 因此,这不会产生替换失败。

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM