繁体   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