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