[英]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.