我正在尝试将通用 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&)
类型的函数不能用 int
和 Silly 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/