这似乎应该经常被问及回答,但我的搜索功能让我失望。
我正在编写一个函数,我想接受某种类型的通用可调用对象(包括裸函数,手动仿函数对象,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
时很常见)。
但这要求函子实现operator()
为const。我不想要那个;我希望它能够接受两者。
我知道我可以声明这个方法的两个副本,一个接受它作为const,一个接受非const,以便涵盖这两种情况。但是我不想这样做,因为评论隐藏了很多我不想复制的代码(包括一些循环结构,因此我无法将它们提取到辅助方法中)不只是移动问题)。
我也知道在调用它之前我可能会欺骗和const_cast
非const的仿函数,但这感觉有潜在的危险(特别是如果函子有意地实现const和non,则会调用错误的方法) -const call operators)。
我已经考虑过将这个仿函数作为std::function
/ boost::function
来接受,但这对于应该是一个简单的问题来说是一个重要的解决方案。 (特别是在仿函数不做任何事情的情况下。)
是否有&#34;权利&#34;如何在不重复算法的情况下满足这些要求?
[注意:我更喜欢不需要C ++ 11的解决方案,尽管我也对C ++ 11的答案感兴趣,因为我在两种语言的项目中都使用类似的结构。]
答案 0 :(得分:3)
您是否尝试过转发层,强制推断限定符?让编译器通过普通的模板实例化机制进行算法复制。
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);
}
包装器应该是完全内联的,并且根本没有运行时成本,除了你可能最终会有更多的模板实例化(因此代码量更大)这一事实,尽管这只会在没有{{ 1}}其中const(或临时)和非const仿函数都被传递。
答案 1 :(得分:1)
在对另一个答案的评论中,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() ) );
为什么我没有立即想到这一点,尽管已经将这个解决方案应用于字符串构建之类的其他事情:它很容易创建复杂性,更难以简化! :)