简体   繁体   English

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

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

I am trying to pass a generic lambda function to a boost::fusion::fold function so that I can iterate all the elements of a boost::fusion::vector. 我试图将通用的lambda函数传递给boost :: fusion :: fold函数,以便我可以迭代boost :: fusion :: vector的所有元素。 My goal is to call a non-const member function from each element in the vector. 我的目标是从向量中的每个元素调用一个非常量成员函数。 The problem is that even though the vector holds non-const values the type deduced by the generic lambda is a const reference. 问题在于,即使向量包含非常量值,由通用lambda推导的类型也是const引用。 This results in my gcc-4.9.0 compiler (using CygWin) complaining that I am discarding the const qualifier. 这导致我的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                                      
}                                                                                              

Now, if instead of the lambda I pass the following functor, the program compiles cleanly 现在,如果我通过以下函子而不是lambda,程序将干净地编译

struct functor {

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

Is this a boost::fusion bug or am I missing something? 这是boost :: fusion错误还是我错过了什么? Thanks in advance! 提前致谢!

There are multiple boost::fusion::fold overloads. 有多个boost::fusion::fold重载。 From boost's svn repo : 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);
}

The compiler is allowed to instantiate the class template result_of::BOOST_FUSION_FOLD_NAME (*) in the return type for all these variants once type deduction and substitution have succeeded, before an overload is selected. 编译器允许实例化类模板result_of::BOOST_FUSION_FOLD_NAME在所有的返回类型,这些变体一旦类型推演和替换(*)已经成功,选择了一个载之前 In this case, the compiler must instantiate this class template in order to determine whether or not the return type is valid. 在这种情况下,编译器必须实例化该类模板,以确定返回类型是否有效。 If substitution (of the template arguments) in the return type leads to an invalid type in the immediate context , the overload is discarded. 如果在返回类型中替换(替换模板参数)在立即上下文中导致无效类型,则将丢弃重载。 This is known as SFINAE. 这称为SFINAE。

(*) This name typically resolves to result_of::fold . (*)此名称通常解析为result_of::fold

The instantiation of one of the overloads that has a Seq const& parameter tries now to determine the return type of the lambda. 现在,具有Seq const&参数的重载之一的实例将尝试确定lambda的返回类型。 However, instantiating the lambda with a Silly const& second argument fails: increment cannot be called on a const object (this is what the compiler tells you). 但是,使用Silly const&第二个参数实例化lambda失败:无法在const对象上调用increment (这是编译器告诉您的)。

If determining the return type fails, this should lead to a substitution failure in the fold overload we're trying to determine the return type of. 如果确定返回类型失败,这将导致我们尝试确定其返回类型的fold重载替换失败。 However, substitution failures due to automatic return type deduction in lambdas and C++14 functions are not in the immediate context of the original template fold : They happen within the function that uses automatic return type deduction (here: the lambda). 但是,由于lambda和C ++ 14函数中的自动返回类型推导而导致的替换失败不在原始模板fold的直接上下文中:它们发生使用自动返回类型推导的函数中(此处为lambda)。

A substitution failure not in the immediate context of the original template is a hard error, it is not a SFINAE-type error that you could recover from. 不在原始模板的直接上下文中发生的替换失败是一个硬错误,它不是可以从中恢复的SFINAE类型的错误。 (SFINAE = SFIICINAE) (SFINAE = SFIICINAE)

If you explicitly specify the return type of the lambda, [](int i, auto& x) -> int {return x.increment(i);} , the function/lambda does not need to be instantiated to determine the return type. 如果您显式指定了lambda的返回类型, [](int i, auto& x) -> int {return x.increment(i);} ,则无需实例化函数/ lambda即可确定返回类型。 It can be determined from the declaration alone. 可以仅从声明中确定。 Therefore, no substitution failure based on the return type happens for any of the overloads, and usual overload resolution can select an appropriate overload. 因此,对于任何重载,都不会发生基于返回类型的替换失败,并且通常的重载解决方案可以选择适当的重载。 The non-const Seq& overload is chosen, and the instantiation of the lambda will be valid. 选择非常量Seq&重载,并且lambda的实例化将有效。

Similarly, for the explicitly written functor: If the return type can be determined without instantiating the function, no error will occur. 同样,对于显式编写的函子:如果可以在不实例化函数的情况下确定返回类型,则不会发生错误。 If you use C++14's return type deduction for ordinary functions, the same problem occurs: 如果将C ++ 14的返回类型推导用于普通函数,则会发生相同的问题:

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

As a side remark: As TC noted in a comment , a hard error also occurs for the following function object type: 附带说明:如TC注释中所述,以下功能对象类型也会发生硬错误:

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

The reason this fails is different, though: Again, all fold overloads need to instantiate the result_of::fold class template with their respective types. 但是,失败的原因有所不同:同样,所有fold重载都需要使用各自的类型实例化result_of::fold类模板。 This class template however does not produce substitution errors in the immediate context: If the function passed cannot be called with the argument types passed, a hard error will occur. 但是,此类模板不会在立即上下文中产生替换错误:如果无法通过传递的参数类型调用传递的函数,则将发生硬错误。

Since a function of the type int(int, Silly&) cannot be called with arguments of the type int and Silly const& , a hard error occurs. 由于不能使用intSilly const&类型的参数调用int(int, Silly&)类型的Silly const& ,因此会发生硬错误。

When writing the apply operator as a template (as in the example with C++14 return type deduction), the declaration of the operator() template can be instantiated for an second argument of type Silly const& ( X will be deduced to be Silly const ). 当将apply运算符作为模板编写时(例如在C ++ 14返回类型推导的示例中),可以为Silly const&类型的第二个参数实例化operator()模板的声明X将推导为Silly const )。 The function definition cannot be instantiated, though, as this will result in the same error as in the OP: Silly::increment requires a non-const Silly object. 但是,不能实例化函数定义 ,因为这将导致与OP中相同的错误: Silly::increment需要一个非常量Silly对象。

The instantiation of the definition of the function however happens only after overload resolution if there is no return type deduction . 但是, 如果没有返回类型推导 ,则函数定义的实例化仅在重载解析之后发生。 Therefore, this will not produce substitution failures. 因此,这不会产生替换失败。

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

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