C ++: merge acceleration with C ++ 14 generic lambdas

I am trying to pass a generic lambda function to the boost :: fusion :: fold function so that I can iterate over all the elements of boost :: fusion :: vector. My goal is to call the non-const member function on each element of the vector. The problem is that although the vector contains non-constant values, the type deduced by the common lambda is a reference to a constant. This leads to my gcc-4.9.0 compiler (using CygWin), complaining that I am dropping the const qualifier.

#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 lambda I pass the following functor, the program compiles purely

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

Is this a boost :: fusion error, or am I missing something? Thanks in advance!

+5
source share
1 answer

There are several overloads of boost::fusion::fold . From 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 result_of::BOOST_FUSION_FOLD_NAME (*) in the return type for all of these options after the deduction and lookup types succeed before overload is selected. In this case, the compiler must instantiate this class template to determine if the return type is valid. If the substitution of (template arguments) in the return type results in an invalid type in the immediate context, the overload is discarded. This is called SFINAE.

(*) This name usually solves result_of::fold .

Creating an instance of one of the overloads with the Seq const& parameter Seq const& now tries to determine the return type of the lambda. However, instantiating a lambda with the argument Silly const& fails: increment cannot be called on the const object (this is what the compiler says).

If the determination of the return type is not performed, this should lead to a replacement error in the fold overload, which we are trying to determine the return type. However, substitution failures due to automatic subtraction of the return type in the lambdas and C ++ 14 functions are not in the immediate context of the original fold template: they occur inside a function that uses automatic type return (here: lambda).

A replacement error in the immediate context of the original template is a tough error; it is not an SFINAE type error from which you can recover. (SFINAE = SFIICINAE)

If you explicitly specify the return type of the lambda, [](int i, auto& x) -> int {return x.increment(i);} , the / lambda function does not need to create an instance to determine the return type. This can only be specified in the declaration. Therefore, no replacement failure based on the return type occurs for any congestion, and the usual congestion resolution may select the appropriate congestion. An overload of non-const Seq& selected, and the lambda implementation will be implemented.

Similarly, for an explicitly written functor: if the return type can be determined without instantiating the function, there will be no error. If you use C ++ 14 type output for regular functions, the same problem arises:

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

As a side note: like the TC noted in comment , a hard error also occurs for the following type of function object:

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

The reason this fails is different: Again, for all fold overloads, you must create an instance of the result_of::fold class template with their corresponding types. However, this template template does not create replacement errors in the immediate context: if the passed function cannot be called with the passed argument types, a severe error will occur.

Since a function of type int(int, Silly&) cannot be called with arguments of type int and Silly const& , a tough error occurs.

When writing an application operator as a template (as in the example with the output type of the returned type as C ++ 14), an operator() template declaration can be created for the second argument of type Silly const& ( X will be output as Silly const ). However, a function definition cannot be created, as this will lead to the same error as in OP: Silly::increment requires a non-const Silly object.

However, the instantiation of a function definition occurs only after overload resolution, if there is no return type inference. Therefore, this will not lead to substitution failures.

+4
source

Source: https://habr.com/ru/post/1204334/


All Articles