簡體   English   中英

Const和非const仿函數

[英]Const and non-const functors

這似乎應該經常被問及回答,但我的搜索功能讓我失望了。

我正在編寫一個函數,我想接受某種類型的通用可調用對象(包括裸函數,手動仿函數對象, bindstd::function ),然后在算法的深度內調用它(即。一個lambda)。

該函數目前聲明如下:

template<typename T, typename F>
size_t do_something(const T& a, const F& f)
{
   T internal_value(a);
   // do some complicated things
   // loop {
   //   loop {
       f(static_cast<const T&>(internal_value), other_stuff);
       // do some more things
   //   }
   // }
   return 42;
}

我通過引用接受仿函數,因為我想保證它不會在函數入口時被復制,因此實際調用了對象的相同實例。 它是一個const引用,因為這是接受臨時對象的唯一方法(在使用手動仿函數或bind時很常見)。

但這需要functor實現operator()為const。 我不想要那個; 我希望它能夠接受兩者。

我知道我可以聲明這個方法的兩個副本,一個接受它作為const,一個接受非const,以便涵蓋這兩種情況。 但是我不想這樣做,因為注釋隱藏了很多我不想復制的代碼(包括一些循環結構,所以我不能在不移動問題的情況下將它們提取到輔助方法) 。

我也知道在調用它之前我可能會欺騙和const_cast函數到非const,但這感覺有潛在危險(特別是如果函子故意實現const和非const調用操作符,則會調用錯誤的方法)。

我已經考慮過將functor作為std::function / boost::function ,但這對於應該是一個簡單的問題來說是一個重要的解決方案。 (特別是在仿函數不做任何事情的情況下。)

有沒有“正確”的方法來滿足這些要求而不是復制算法?

[注意:我更喜歡不需要C ++ 11的解決方案,盡管我也對C ++ 11的答案感興趣,因為我在兩種語言的項目中都使用類似的結構。

您是否嘗試過轉發層,強制推斷限定符? 編譯器通過正常的模板實例化機制進行算法復制。

template<typename T, typename F>
size_t do_something_impl(const T& a, F& f)
{
   T internal_value(a);
   const T& c_iv = interval_value;
   // do some complicated things
   // loop {
   //   loop {
       f(c_iv, other_stuff);
       // do some more things
   //   }
   // }
   return 42;
}

template<typename T, typename F>
size_t do_something(const T& a, F& f)
{
   return do_something_impl<T,F>(a, f);
}

template<typename T, typename F>
size_t do_something(const T& a, const F& f)
{
   return do_something_impl<T,const F>(a, f);
}

演示: http//ideone.com/owj6oB

包裝器應該是完全內聯的,並且根本沒有運行時成本,除了你可能最終會有更多的模板實例化(因此更大的代碼大小),盡管只有在沒有operator()() const類型時才會發生這種情況。 operator()() const傳遞const(或臨時)和非const仿函數的const。

回答新的寬松要求。

在對另一個答復的評論中,OP已經澄清/改變了要求......

“我可以要求如果函數作為臨時函數傳入,那么它必須有一個operator()const。 我只是不想限制它,如果一個仿函數沒有作為臨時傳入(當然也不是const非臨時),那么它允許有一個非const運算符() ,這將被稱為“

這根本不是問題 :只提供一個接受臨時的重載。

有幾種方法可以區分原始的基本實現,例如在C ++ 11中有一個額外的默認模板參數,在C ++ 03中有一個額外的默認普通函數參數。

但最明顯的是恕我直言,只是給它一個不同的名稱,然后提供一個重載的包裝:

template<typename T, typename F>
size_t do_something_impl( T const& a, F& f)
{
   T internal_value(a);
   // do some complicated things
   // loop {
   //   loop {
       f(static_cast<const T&>(internal_value), other_stuff);
       // do some more things
   //   }
   // }
   return 42;
}

template<typename T, typename F>
size_t do_something( T const& a, F const& f)
{ return do_something_impl( a, f ); }

template<typename T, typename F>
size_t do_something( T const& a, F& f)
{ return do_something_impl( a, f ); }

注意:沒有必要顯式指定do_something_impl實例化,因為它是從左值參數推斷出來的。

這種方法的主要特點是它支持更簡單的調用,代價是當它具有非const operator()時不支持臨時的參數。

原始答案:

您的主要目標是避免復制仿函數,並接受臨時的實際參數。

在C ++ 11中,您可以使用右值引用, &&

對於C ++ 03,問題是作為實際參數的臨時仿函數實例,其中該仿函數具有非const operator()

一種解決方案是將負擔傳遞給客戶端代碼編程器,例如

  • 要求實際參數為左值,而不是臨時值, 或者

  • 要求顯式指定參數是臨時的,然后將其作為const引用並使用const_cast

例:

template<typename T, typename F>
size_t do_something( T const& a, F& f)
{
   T internal_value(a);
   // do some complicated things
   // loop {
   //   loop {
       f(static_cast<const T&>(internal_value), other_stuff);
       // do some more things
   //   }
   // }
   return 42;
}

enum With_temp { with_temp };

template<typename T, typename F>
size_t do_something( T const& a, With_temp, F const& f )
{
    return do_something( a, const_cast<F&>( f ) );
}

如果希望直接支持const類型的臨時工,為了簡化客戶端代碼程序員的生活,這種罕見情況,那么一個解決方案就是添加額外的重載:

enum With_const_temp { with_const_temp };

template<typename T, typename F>
size_t do_something( T const& a, With_const_temp, F const& f )
{
    return do_something( a, f );
}

感謝Steve Jessop和Ben Voigt對此案的討論。


另一種更通用的C ++ 03方法是提供以下兩個小函數:

template< class Type >
Type const& ref( Type const& v ) { return v; }

template< class Type >
Type& non_const_ref( Type const& v ) { return const_cast<T&>( v ); }

然后do_something ,如上面在這個答案中給出的,可以被稱為...

do_something( a, ref( MyFunctor() ) );
do_something( a, non_const_ref( MyFunctor() ) );

為什么我沒有立即想到這一點,盡管已經將這個解決方案用於其他事情,如字符串構建:它很容易創建復雜性,更難以簡化! :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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