[英]Const and non-const functors
這似乎應該經常被問及回答,但我的搜索功能讓我失望了。
我正在編寫一個函數,我想接受某種類型的通用可調用對象(包括裸函數,手動仿函數對象, bind
或std::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.