禁止显式指定的模板

Disallow templates that are explicitly specified

本文关键字:禁止      更新时间:2023-10-16

我想要一个宽模板,除了我明确指定了大小写时,它"做任何需要做的事情"。

具体来说,我正在重载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)

几乎可以肯定的是,通过重载分辨率选择模板的原因是您传入的两个参数并不是真正size_t。他们可能是int,或者别的什么。如果它们真的是size_t,那么我希望你的非模板重载被选中。清理它应该使事情正常工作,按原样,但它足够简单,在任何情况下都可以使它工作。

在这种情况下,通常的方法是在传入参数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识别的内容时,才让此模板参与重载解析。最佳方法取决于您特定类的要求。