簡體   English   中英

C ++:使用C ++ 14通用Lambda增強融合折疊

[英]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類模板。 但是,此類模板不會在立即上下文中產生替換錯誤:如果無法通過傳遞的參數類型調用傳遞的函數,則將發生硬錯誤。

由於不能使用intSilly 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM