我希望有一个宽广的模板,“做任何需要的事情”,除非我明确指明了这个案例。
具体来说,我正在重载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)
答案 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_v
和std::enable_if_t
快捷方式仅在C ++ 17(以及std::void_t
)中可用,但如果有必要,它很简单,可以在早期标准中重新发明该方向盘。
您也可以尝试以相反的方向使用SFINAE:仅当Iterator
解析为std::iterator_traits
识别的内容时,才允许此模板参与重载解析。最好的方法取决于您特定班级的要求。