C++:使用 C++14 通用 lambda boost fusion fold

标签 c++ boost lambda c++14 auto

我正在尝试将通用 lambda 函数传递给 boost::fusion::fold 函数,以便我可以迭代 boost::fusion::vector 的所有元素。我的目标是从 vector 中的每个元素调用一个非常量成员函数。问题在于,即使 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's 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 (*),< em>在 重载被选择之前。在这种情况下,编译器必须实例化此类模板以确定返回类型是否有效。如果返回类型中的(模板参数的)替换导致无效类型在直接上下文中,则重载被丢弃。这被称为 SFINAE。

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

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

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

顺便说一句:作为T.C.comment 中注明,对于以下函数对象类型也会发生硬错误:

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

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

由于 int(int, Silly&) 类型的函数不能用 intSilly const& 类型的参数调用,所以发生硬错误。

将应用运算符编写为模板时(如 C++14 返回类型推导的示例),operator() 模板的声明可以是为 Silly const& 类型的第二个参数实例化(X 将被推断为 Silly const)。但是,函数 definition 无法实例化,因为这将导致与 OP 中相同的错误:Silly::increment requires a non-const Silly 对象。

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

关于C++:使用 C++14 通用 lambda boost fusion fold ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26280503/

相关文章:

c++ - 在函数内部使用裸括号是一种好习惯吗?

c++ - boost .Python : expose class member which is a pointer

c++ - 如何运行 Boost Test 并生成 Minidump?

amazon-web-services - 地形 (AWS) : How to get DNS name of Load Balancer by its ARN?

c++ - 通过引用将对象参数传递给仿函数

c++ - g++ 和 clang++ 变量模板和 SFINAE 的不同行为

python - boost .python : Calling function of a base class from Python

c++ - 如何在 C++11 中的单个表达式中声明'n'run lambda?

java - 如何使用 Java 8 Streams 填充 ArrayList 直到达到指定的最大项目数?

c++ - srand 不是每次都初始化一个新的输出