lin*_*ver 5 c++ lambda boost auto c++14
我试图将一个通用的lambda函数传递给boost :: fusion :: fold函数,这样我就可以迭代boost :: fusion :: vector的所有元素.我的目标是从向量中的每个元素调用一个非const成员函数.问题是即使向量包含非const值,通用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
}
Run Code Online (Sandbox Code Playgroud)
现在,如果不是lambda而是传递以下仿函数,程序将完全编译
struct functor {
template <class X>
int operator()(int i, X& x) {
return x.increment(i);
}
};
Run Code Online (Sandbox Code Playgroud)
这是一个boost :: fusion bug还是我错过了什么?提前致谢!
有多个boost::fusion::fold重载.来自boost的svn回购:
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);
}
Run Code Online (Sandbox Code Playgroud)
在选择重载之前,一旦类型推断和替换成功,允许编译器在返回类型中为所有这些变量实例化类模板result_of::BOOST_FUSION_FOLD_NAME (*).在这种情况下,编译器必须实例化此类模板,以确定返回类型是否有效.如果返回类型中的(模板参数的)替换导致直接上下文中的无效类型,则丢弃重载.这被称为SFINAE.
(*)此名称通常解析为result_of::fold.
具有Seq const&参数的其中一个重载的实例化现在尝试确定lambda的返回类型.但是,使用Silly const&第二个参数实例化lambda 失败:increment无法在const对象上调用(这是编译器告诉您的).
如果确定返回类型失败,这将导致fold我们尝试确定返回类型的重载中的替换失败.但是,由于lambdas和C++ 14函数中的自动返回类型推导导致的替换失败不在原始模板的直接上下文中fold:它们发生在使用自动返回类型推导的函数中(此处:lambda).
替换失败不在原始模板的直接上下文中是一个硬错误,它不是您可以从中恢复的SFINAE类型错误.(SFINAE = SFIICINAE)
如果显式指定lambda的返回类型,则[](int i, auto& x) -> int {return x.increment(i);}不需要实例化函数/ lambda来确定返回类型.它可以仅从声明中确定.因此,对于任何重载都不会发生基于返回类型的替换失败,并且通常的重载决策可以选择适当的重载.选择非const Seq&重载,并且lambda的实例化将是有效的.
类似地,对于显式编写的仿函数:如果可以在不实例化函数的情况下确定返回类型,则不会发生错误.如果对普通函数使用C++ 14的返回类型推导,则会出现同样的问题:
struct functor {
template <class X>
auto operator()(int i, X& x) {
return x.increment(i);
}
};
Run Code Online (Sandbox Code Playgroud)
作为旁注:正如TC在注释中指出的那样,以下函数对象类型也会出现硬错误:
struct functor {
int operator()(int i, Silly& x) {
return x.increment(i);
}
};
Run Code Online (Sandbox Code Playgroud)
但是失败的原因是不同的:同样,所有fold重载都需要result_of::fold使用各自的类型来实例化类模板.但是,此类模板不会在直接上下文中产生替换错误:如果传递的参数类型无法调用传递的函数,则会发生硬错误.
由于这种类型的功能int(int, Silly&)不能与类型的参数来调用int和Silly const&,发生硬错误.
当写入操作者应用作为模板(如在与C++ 14返回类型推演的例子),则声明的的operator()模板可以被实例化类型的第二个参数Silly const&(X将被推断为Silly const).但是,函数定义无法实例化,因为这将导致与OP中相同的错误:Silly::increment需要非const Silly对象.
然而,如果没有返回类型推断,则仅在重载解析之后才发生函数定义的实例化.因此,这不会产生替代失败.