在这种情况下,有什么方法可以改进模板参数推导吗

Is there any way to improve template argument deduction in this case?

本文关键字:参数 这种情况下 什么 方法      更新时间:2023-10-16

我有一个容器类,可以对各种格式的像素进行操作。有些格式只是将像素存储在内存中,因此容器的引用类型是Pixel&。其他格式将像素存储在压缩字节中。没有什么可以返回引用的,所以这种情况下的引用类型是代理类型。我的所有代理类型都有一个名为value_type的嵌入式typedef,它是底层像素的类型。

当我试图编写对像素进行运算的函子时,遇到了一个问题。例如,我希望能够写这样的东西:

std::for_each( container.begin(), container.end(), Incrementer() );

我已经能够以两种不同的方式实现这一点,但我不喜欢其中的任何一种,所以我想知道这是否可以改进。

第一种方法是:

struct Incrementer {
  template <typename Pixel>
  void operator()( Pixel& p ) {
    p = p + 1;
  } 
  template <typename Proxy>
  void operator()( Proxy p, typename Proxy::value_type* dummy=0 ) {
    p = p + 1;
  }
};

基本思想是我有两个重载,一个用于容器返回对像素的引用的情况,另一个用于返回代理的情况。我需要第二个重载的伪参数,这样引用的情况就明确了。问题是,我使用的每个函子都需要这两个重载(包括伪参数(,所以我认为接口相当难看。

也许我可以写一些模板魔术来整理参数类型,但我一直遇到编译器永远不会推断引用参数的问题,所以我需要提供一个带有非常量引用的重载。另一方面,代理是临时的,因此不能通过非常数引用传递。这让我陷入了两次过载的困境。

我有什么东西不见了吗?

后备解决方案类似于:

  template < typename PixelReference >
  struct Incrementer {
    void operator()( PixelReference p ) {
      p = p + 1;
    }
  };
  std::for_each( container.begin(), container.end(),      
                 Incrementer< pixel_traits<Pixel,Format>::reference >() );

(假设我有一个像素的特征类(。现在,我必须在创建函子时指定引用类型,这通常很麻烦。

我有什么办法可以改进这两种选择吗?我把这个容器作为一个库来编写,所以我试图让编写和使用函子尽可能简单。

谢谢。

使用std::for_each()可能会阻碍您。如果您使用std::transform()呢?它甚至支持相同的就地修改:

transform( container.begin(), container.end(), container.begin(), Incrementer() );

那么Incrementer::operator()只需要为传递值实现,因为它将不再显式地修改原地的像素。当然,您需要支持为迭代器分配代理值,但这可能并不那么困难。

我认为有几种方法可以解决您的问题。

1.在您的示例中,两个operator()是相同的,所以您已经在使用编译时多态性,而您的问题是operator() 的多重定义

编辑

代码应该是这样的:

struct Incrementer {
  template <typename Pixel_or_Proxy>
  void operator()( Pixel_or_Proxy& p ) {
    p = p + 1;
  }
};

2.使用boost enable_if&disable_if并在boost.mpl boost_mpl_HAS_XXX_TRAIT_DEF 的帮助下检查value_type

3.如果你的代理是一个模板,你可以为它创建一个更好的过载:

template<class T>
void operator()(T t) { 
  // normal operation
}
template<class T>
void operator()(Proxy<T> t) {
  // proxy operation
}

编辑

对于选项2:由于方法的主体是相等的,您只需要编写一个模板方法。类型名PixelProxy只是对编译器没有任何语义意义的名称,只是对用户。编译器只是用给定的类型实例化模板方法。如果你有不同的方法,你需要通过重载来选择其中一个。这可以通过enable_if和朋友来完成,也可以在类型特征的帮助下通过额外的调用来完成。