[英]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
类模板。 但是,此类模板不会在立即上下文中产生替换错误:如果无法通过传递的参数类型调用传递的函数,则将发生硬错误。
由于不能使用int
和Silly 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.