Const和非常量函子
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;
}
我通过引用接受函子,因为我想保证它不会在进入函数时被复制,从而实际调用对象的同一实例。它是一个常量引用,因为这是接受临时对象的唯一方法(在使用手动滚动函子或bind
时很常见)。
但这需要函子将operator()
实现为const。我不想要求这样;我希望它能同时接受这两种情况。
我知道我可以声明这个方法的两个副本,一个作为const接受,另一个作为非常数接受,以涵盖这两种情况。但我不想这么做,因为注释隐藏了很多我不想复制的代码(包括一些循环结构,所以我不能在不移动问题的情况下将它们提取到第二个方法)。
我也知道,在调用函子之前,我可能会欺骗并const_cast
函子为非常数,但这感觉有潜在的危险(尤其是如果函子有意同时实现const和非常数调用运算符,则会调用错误的方法)。
我已经考虑过接受函子作为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
的类型同时传递const(或临时)和nonconst函数时才会发生这种情况。
回答新的放宽要求
在对另一个答案的评论中,OP已将要求澄清/更改为…
“如果函子作为临时函数传入,我可以要求则它必须具有运算符()常量。我只是不想限制它这样,如果函子不是作为临时(和当然也不是const非临时),则允许一个非常数运算符(),这将被称为”
这就是根本不是问题:只需提供一个接受临时的重载。
有几种方法可以区分原始的基本实现,例如在C++11中使用额外的默认模板参数,在C++03中使用额外默认的普通函数参数。
但最清楚的是,IMHO只是给它一个不同的名称,然后提供一个过载的包装器:
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() ) );
为什么我没有立即想到这一点,尽管我已经将这个解决方案用于其他事情,如字符串构建:它很容易创建复杂性,更难简化!:)
- #定义c-预处理器常量..我做错了什么
- 用C++中的一个变量定义一个常量
- 什么时候在C++中返回常量引用是个好主意
- 代理对象的常量正确性
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 通过多个头文件使用常量变量
- 在cuda线程之间共享大量常量数据
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 是默认情况下分配给char数组常量的值
- 私有类型的静态常量成员
- OpenGL大的3D纹理(>2GB)非常慢
- 类似枚举的计算常量
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 使用常量键但非常量值进行映射
- 为什么`is_open()`非常常量
- 从getter方法返回常量和非常量值
- 阻止const类函数在引用成员上调用非常常量类函数
- C++初始化非常大的常量数组,最佳实践
- 提高c++中非常大的常量的可读性