在这种情况下,有没有办法改进模板参数推导?

时间:2013-06-03 13:44:23

标签: c++ templates

我有一个容器类,可以处理各种格式的像素。某些格式只将像素存储在内存中,因此容器的引用类型为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;
  }
};

基本思想是我有两个重载,一个用于容器返回像素引用的情况,另一个用于返回代理。我需要第二次重载的伪参数,以便参考案例是明确的。这个问题是我使用的每个仿函数都需要这两个重载(包括伪参数),所以我觉得界面相当丑陋。

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

我有什么遗失的吗?

后备解决方案类似于:

  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 >() );

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

我有什么方法可以改进其中任何一种选择吗?我正在把这个容器写成一个库,所以我试着尽可能简单地编写和使用仿函数。

感谢。

2 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:0)

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

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和朋友或额外的call with help of type traits完成。