禁止显式指定的模板

时间:2018-04-08 16:36:04

标签: c++

我希望有一个宽广的模板,“做任何需要的事情”,除非我明确指明了这个案例。

具体来说,我正在重载operator()以将其用于多维矩阵的矩阵索引。我还想允许使用迭代器指定任意数量的索引。理想情况下,我有以下签名:

operator()(size_t);
operator()(size_t,size_t);
operator()(size_t,size_t,size_t);
...

template<class Iterator> operator()(Iterator,Iterator);

问题是永远不会达到operator()(size_t,size_t),因为编译器也能够模板template<class Iterator> operator()(Iterator,Iterator)。我怎么能避免这个?

一个明显的解决方案是使用std::vector<size_t>::iterator代替Iterator。我试过这个,但这缩小了其他地方的用法。

一个最小的例子:

#include <iostream>

class Foo
{
private:
  double data[9];

public:
  Foo(){};

  double& operator()(size_t i, size_t j)
  {
    std::cout << "operator()(size_t i, size_t j)" << std::endl;
    return data[0];
  }

  template<class Iterator>
  double& operator()(Iterator first, Iterator last)
  {
    std::cout << "operator()(Iterator first, Iterator last)" << std::endl;
    return data[0];
  }

};


int main()
{
  Foo bar;

  bar(0,1);
}

输出:

operator()(Iterator first, Iterator last)

虽然我想要这个案例输出

operator()(size_t i, size_t j)

1 个答案:

答案 0 :(得分:3)

通过重载决策选择模板的原因几乎可以肯定,因为您传入的两个参数实际上不是size_t。他们可能是int或其他东西。如果它们真的是size_t s,那么我希望你的非模板重载被选中。清理它应该会使事情变得有效,但是它很简单,无论如何都能使它工作。

在这种情况下,通常的做法是当传入的参数为size_t时,使用SFINAE排除模板参与重载解析。有些东西(使用C ++ 17):

template<class Iterator,
         typename=std::enable_if_t<
                  std::negation_v<std::is_integral_v<Iterator>>>> operator()(Iterator,Iterator)
{
   // ...
}

这是你的出发点。使用std::is_same_v<Iterator,size_t>很有吸引力,但是你很快就会发现只有当你准确传递size_t时这才有效。如果你不小心的话,int很容易进入那里,在这种情况下,这将会崩溃。所以你可能需要使用std::is_integral_v。希望您不会在任何地方传递浮点值,并依赖它们被截断为下一个最接近的整数值。如果你这样做,你将不得不进一步调整。

std::is_integral_vstd::enable_if_t快捷方式仅在C ++ 17(以及std::void_t)中可用,但如果有必要,它很简单,可以在早期标准中重新发明该方向盘。

您也可以尝试以相反的方向使用SFINAE:仅当Iterator解析为std::iterator_traits识别的内容时,才允许此模板参与重载解析。最好的方法取决于您特定班级的要求。